3131
3232# stdlib
3333from io import TextIOBase
34- from typing import IO , Any , Dict , Iterable , List , Mapping , MutableMapping , Sequence , Union , cast
34+ from typing import IO , Any , Dict , Iterable , List , Mapping , MutableMapping , Optional , Sequence , Union
3535
3636# 3rd party
37- import tinycss2
38- import tinycss2 .ast
37+ import tinycss2 # type: ignore[import-untyped]
38+ import tinycss2 .ast # type: ignore[import-untyped]
3939from domdf_python_tools .paths import PathPlus
4040from domdf_python_tools .typing import PathLike
4141from domdf_python_tools .words import TAB
4545from dict2css .serializer import CSSSerializer
4646
4747__author__ : str = "Dominic Davis-Foster"
48- __copyright__ : str = "2020-2021 Dominic Davis-Foster"
48+ __copyright__ : str = "2020-2026 Dominic Davis-Foster"
4949__license__ : str = "MIT License"
5050__version__ : str = "0.3.0.post1"
5151__email__ : str = "dominic@davis-foster.co.uk"
5757 "dump" ,
5858 "loads" ,
5959 "load" ,
60- "StyleSheet" ,
61- "make_style" ,
6260 ]
6361
62+ # TODO: allow int indent like json.dumps etc.
63+
6464IMPORTANT = "important"
6565"""
6666The string ``'important'``.
7373
7474Style = Mapping [str , Property ]
7575"""
76- Type annotation representing a style for :func:`~.make_style ` and :func:`~.dumps `.
76+ Type annotation representing a style for :func:`~.dumps ` and :func:`~.dump `.
7777
7878The keys are CSS properties.
7979
@@ -89,9 +89,11 @@ def dumps(
8989 styles : Mapping [str , Union [Style , Mapping ]],
9090 * ,
9191 indent : str = TAB ,
92- trailing_semicolon : bool = False ,
92+ trailing_semicolon : Optional [ bool ] = None ,
9393 indent_closing_brace : bool = False ,
9494 minify : bool = False ,
95+ sort_keys : bool = False ,
96+ check_circular : bool = True ,
9597 ) -> str :
9698 r"""
9799 Construct a cascading style sheet from a dictionary.
@@ -135,6 +137,8 @@ def dumps(
135137 :param trailing_semicolon: Whether to add a semicolon to the end of the final property.
136138 :param indent_closing_brace:
137139 :param minify: Minify the CSS. Overrides all other options.
140+ :param sort_keys: Sort dictionary keys alphabetically.
141+ :param check_circular: Check for circular references.
138142
139143 :return: The style sheet as a string.
140144
@@ -146,37 +150,23 @@ def dumps(
146150 trailing_semicolon = trailing_semicolon ,
147151 indent_closing_brace = indent_closing_brace ,
148152 minify = minify ,
153+ sort_keys = sort_keys ,
154+ check_circular = check_circular ,
149155 )
150156
151- stylesheet : str = ''
152-
153- with serializer .use ():
154- sheet = StyleSheet ()
155-
156- for selector , style in styles .items ():
157- if selector .startswith ("@media" ):
158- sheet .add_media_styles (selector .split ("@media" )[1 ].strip (), cast (Mapping [str , Style ], style ))
159- elif selector .startswith ('@' ):
160- raise NotImplementedError ("Only @media at-rules are supported at this time." )
161- else :
162- sheet .add_style (selector , cast (Style , style ))
163-
164- stylesheet = sheet .tostring ()
165-
166- if not serializer .minify :
167- stylesheet = stylesheet .replace ('}' , "}\n " )
168-
169- return stylesheet
157+ return serializer .encode (styles )
170158
171159
172160def dump (
173161 styles : Mapping [str , Union [Style , Mapping ]],
174162 fp : Union [PathLike , IO ],
175163 * ,
176164 indent : str = TAB ,
177- trailing_semicolon : bool = False ,
165+ trailing_semicolon : Optional [ bool ] = None ,
178166 indent_closing_brace : bool = False ,
179167 minify : bool = False ,
168+ sort_keys : bool = False ,
169+ check_circular : bool = True ,
180170 ) -> None :
181171 r"""
182172 Construct a style sheet from a dictionary and write it to ``fp``.
@@ -221,6 +211,8 @@ def dump(
221211 :param trailing_semicolon: Whether to add a semicolon to the end of the final property.
222212 :param indent_closing_brace:
223213 :param minify: Minify the CSS. Overrides all other options.
214+ :param sort_keys: Sort dictionary keys alphabetically.
215+ :param check_circular: Check for circular references.
224216
225217 .. versionchanged:: 0.2.0
226218
@@ -234,6 +226,8 @@ def dump(
234226 indent = indent ,
235227 trailing_semicolon = trailing_semicolon ,
236228 indent_closing_brace = indent_closing_brace ,
229+ sort_keys = sort_keys ,
230+ check_circular = check_circular ,
237231 minify = minify ,
238232 )
239233
@@ -279,7 +273,7 @@ def parse_style(style: List[tinycss2.ast.Node]) -> MutableMapping[str, Property]
279273 styles_dict [_serialize (rule .prelude )] = parse_style (rule .content )
280274
281275 elif isinstance (rule , tinycss2 .ast .AtRule ):
282- at_rule_styles = styles_dict [f"@{ rule .at_keyword } { _serialize (rule .prelude )} " ] = at_rule_styles
276+ at_rule_styles = styles_dict [f"@{ rule .at_keyword } { _serialize (rule .prelude )} " ]
283277
284278 for child in tinycss2 .parse_blocks_contents (rule .content , skip_comments = True , skip_whitespace = True ):
285279 at_rule_styles [_serialize (child .prelude )] = parse_style (child .content )
@@ -290,7 +284,7 @@ def parse_style(style: List[tinycss2.ast.Node]) -> MutableMapping[str, Property]
290284 return styles_dict
291285
292286
293- def _serialize (nodes : Iterable [tinycss2 .ast .Node ]):
287+ def _serialize (nodes : Iterable [tinycss2 .ast .Node ]) -> str :
294288 return tinycss2 .serialize (nodes ).strip ()
295289
296290
@@ -311,93 +305,3 @@ def load(fp: Union[PathLike, IO]) -> MutableMapping[str, MutableMapping[str, Any
311305 styles = PathPlus (fp ).read_text ()
312306
313307 return loads (styles )
314-
315-
316- class StyleSheet (css_parser .css .CSSStyleSheet ):
317- r"""
318- Represents a CSS style sheet.
319-
320- .. raw:: latex
321-
322- \nopagebreak
323-
324- .. autosummary-widths:: 7/16
325-
326- """
327-
328- def __init__ (self ):
329- super ().__init__ (validating = False )
330-
331- def add (self , rule : css_parser .css .CSSRule ) -> int :
332- """
333- Add the ``rule`` to the style sheet.
334-
335- :param rule:
336- :type rule: :class:`css_parser.css.CSSRule`
337- """
338-
339- return super ().add (rule )
340-
341- def add_style (
342- self ,
343- selector : str ,
344- styles : Style ,
345- ) -> None :
346- """
347- Add a style to the style sheet.
348-
349- :param selector:
350- :param styles:
351- """
352-
353- self .add (make_style (selector , styles ))
354-
355- def add_media_styles (
356- self ,
357- media_query : str ,
358- styles : Mapping [str , Style ],
359- ) -> None :
360- """
361- Add a set of styles for a media query to the style sheet.
362-
363- .. versionadded:: 0.2.0
364-
365- :param media_query:
366- :param styles:
367- """
368-
369- media = css_parser .css .CSSMediaRule (media_query )
370-
371- for selector , style in styles .items ():
372- media .add (make_style (selector , style ))
373-
374- self .add (media )
375-
376- def tostring (self ) -> str :
377- """
378- Returns the style sheet as a string.
379- """
380-
381- return self .cssText .decode ("UTF-8" )
382-
383-
384- def make_style (selector : str , styles : Style ) -> css_parser .css .CSSStyleRule :
385- """
386- Create a CSS Style Rule from a dictionary.
387-
388- :param selector:
389- :param styles:
390-
391- :rtype: :class:`css_parser.css.CSSStyleRule`
392- """
393-
394- style = css_parser .css .CSSStyleDeclaration ()
395- style .validating = False
396-
397- for name , properties in styles .items ():
398- if isinstance (properties , Sequence ) and not isinstance (properties , str ):
399- style [name ] = tuple (str (x ) for x in properties )
400- else :
401- style [name ] = str (properties )
402-
403- return css_parser .css .CSSStyleRule (selectorText = selector , style = style )
0 commit comments