From 6f8fc37551e60e1abbaa292e5ff28925d802615a Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Thu, 7 May 2026 10:07:29 +0100 Subject: [PATCH 01/13] Switch parser backend to tinycss2 --- dict2css/__init__.py | 52 ++++++++++++++++++++---------------------- dict2css/serializer.py | 7 ------ doc-source/conf.py | 1 - pyproject.toml | 3 --- repo_helper.yml | 4 ---- requirements.txt | 4 +--- tests/test_dict2css.py | 1 - tox.ini | 3 --- 8 files changed, 26 insertions(+), 49 deletions(-) diff --git a/dict2css/__init__.py b/dict2css/__init__.py index 3ad1289..c51e520 100644 --- a/dict2css/__init__.py +++ b/dict2css/__init__.py @@ -31,19 +31,15 @@ # stdlib from io import TextIOBase -from typing import IO, Any, Dict, Mapping, MutableMapping, Sequence, Union, cast +from typing import IO, Any, Dict, Iterable, List, Mapping, MutableMapping, Sequence, Union, cast # 3rd party +import tinycss2 +import tinycss2.ast from domdf_python_tools.paths import PathPlus from domdf_python_tools.typing import PathLike from domdf_python_tools.words import TAB -try: - # 3rd party - import css_parser # type: ignore -except ImportError: # pragma: no cover - import cssutils as css_parser # type: ignore - # this package from dict2css.helpers import em, px, rem # noqa: F401 from dict2css.serializer import CSSSerializer @@ -258,37 +254,35 @@ def loads(styles: str) -> MutableMapping[str, MutableMapping[str, Any]]: :return: The style sheet as a dictionary. """ - parser = css_parser.CSSParser(validate=False) - stylesheet: css_parser.css.CSSStyleSheet = parser.parseString(styles) + stylesheet = tinycss2.parse_blocks_contents(styles, skip_comments=True, skip_whitespace=True) styles_dict: MutableMapping[str, MutableMapping[str, Any]] = {} - def parse_style(style: css_parser.css.CSSStyleDeclaration) -> MutableMapping[str, Property]: + def parse_style(style: List[tinycss2.ast.Node]) -> MutableMapping[str, Property]: style_dict: Dict[str, Property] = {} - prop: css_parser.css.Property - for prop in style.children(): - if prop.priority: - style_dict[prop.name] = (prop.value, prop.priority) + prop: Union[tinycss2.ast.ParseError, tinycss2.ast.Declaration] + for prop in tinycss2.parse_declaration_list(style, skip_comments=True, skip_whitespace=True): + if isinstance(prop, tinycss2.ast.ParseError): + raise ValueError(prop) + + if prop.important: + style_dict[prop.name.strip()] = (_serialize(prop.value), IMPORTANT) else: - style_dict[prop.name] = prop.value + style_dict[prop.name.strip()] = _serialize(prop.value) return style_dict - rule: css_parser.css.CSSRule - for rule in stylesheet.cssRules: - if isinstance(rule, css_parser.css.CSSStyleRule): - styles_dict[rule.selectorText] = parse_style(rule.style) - - elif isinstance(rule, css_parser.css.CSSMediaRule): - styles_dict[f"@media {rule.media.mediaText}"] = {} + rule: tinycss2.ast.Node + for rule in stylesheet: + if isinstance(rule, tinycss2.ast.QualifiedRule): + styles_dict[_serialize(rule.prelude)] = parse_style(rule.content) - for child in rule.cssRules: - styles_dict[f"@media {rule.media.mediaText}"][child.selectorText] = parse_style(child.style) + elif isinstance(rule, tinycss2.ast.AtRule): + at_rule_styles = styles_dict[f"@{rule.at_keyword} {_serialize(rule.prelude)}"] = at_rule_styles - elif isinstance(rule, (css_parser.css.CSSComment)): # pragma: no cover - # Ignore these classes - pass + for child in tinycss2.parse_blocks_contents(rule.content, skip_comments=True, skip_whitespace=True): + at_rule_styles[_serialize(child.prelude)] = parse_style(child.content) else: raise NotImplementedError(rule) @@ -296,6 +290,10 @@ def parse_style(style: css_parser.css.CSSStyleDeclaration) -> MutableMapping[str return styles_dict +def _serialize(nodes: Iterable[tinycss2.ast.Node]): + return tinycss2.serialize(nodes).strip() + + def load(fp: Union[PathLike, IO]) -> MutableMapping[str, MutableMapping[str, Any]]: r""" Parse a cascading style sheet from the given file and return its dictionary representation. diff --git a/dict2css/serializer.py b/dict2css/serializer.py index d904228..c3414e4 100644 --- a/dict2css/serializer.py +++ b/dict2css/serializer.py @@ -35,13 +35,6 @@ # 3rd party from domdf_python_tools.words import TAB -try: - # 3rd party - import css_parser # type: ignore -except ImportError: # pragma: no cover - # 3rd party - import cssutils as css_parser # type: ignore - __all__ = ["CSSSerializer"] diff --git a/doc-source/conf.py b/doc-source/conf.py index d8b0a88..8c2a0f8 100644 --- a/doc-source/conf.py +++ b/doc-source/conf.py @@ -101,5 +101,4 @@ def setup(app): nitpicky = True latex_elements["preamble"] = "\\raggedbottom\n\\widowpenalty10000" -ignore_missing_xrefs = ["^css_parser\\."] changelog_sections_numbered = False diff --git a/pyproject.toml b/pyproject.toml index 42e57ff..64b8c56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,9 +139,6 @@ reformat = true [tool.snippet-fmt.languages.json] -[tool.dep_checker] -allowed_unused = [ "cssutils",] - [tool.dependency-dash."requirements.txt"] order = 10 diff --git a/repo_helper.yml b/repo_helper.yml index a29fa08..be33fbb 100644 --- a/repo_helper.yml +++ b/repo_helper.yml @@ -40,7 +40,6 @@ classifiers: sphinx_conf_epilogue: - nitpicky = True - latex_elements["preamble"] = "\\raggedbottom\n\\widowpenalty10000" - - ignore_missing_xrefs = ["^css_parser\\."] - changelog_sections_numbered = False exclude_files: @@ -54,6 +53,3 @@ extra_sphinx_extensions: - sphinx_toolbox_experimental.missing_xref - sphinx_toolbox_experimental.changelog - latex_mu - -tox_unmanaged: - - testenv diff --git a/requirements.txt b/requirements.txt index 5b4d1d8..241bfb1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,2 @@ -# css-parser>=1.0.6 -# Later cssutils polluted by AI and the new licence-breaking chardet -cssutils<=2.11.0,>=2.2.0 domdf-python-tools>=2.2.0 +tinycss2>=1.2.1 diff --git a/tests/test_dict2css.py b/tests/test_dict2css.py index 42301cb..3670135 100644 --- a/tests/test_dict2css.py +++ b/tests/test_dict2css.py @@ -2,7 +2,6 @@ from typing import Dict, Mapping, MutableMapping # 3rd party -import cssutils # type: ignore import pytest from coincidence.regressions import AdvancedDataRegressionFixture, AdvancedFileRegressionFixture from domdf_python_tools.paths import PathPlus diff --git a/tox.ini b/tox.ini index c249c9e..bf50f19 100644 --- a/tox.ini +++ b/tox.ini @@ -211,7 +211,4 @@ setenv = deps = -r{toxinidir}/tests/requirements.txt commands = python --version - python -m pip uninstall css-parser -y python -m pytest --cov=dict2css -r aR tests/ {posargs} - python -m pip install "css-parser>=1.0.6" - python -m pytest --cov=dict2css -r aR tests/ --cov-append {posargs} From e6285d43193680817035ad01b677a01ccc73f747 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Thu, 7 May 2026 18:14:03 +0100 Subject: [PATCH 02/13] Add serializer based on ujson5 --- dict2css/__init__.py | 144 ++-------- dict2css/serializer.py | 334 ++++++++++++++++++++--- tests/test_dict2css.py | 45 +-- tests/test_dict2css_/test_stylesheet.css | 11 - 4 files changed, 329 insertions(+), 205 deletions(-) delete mode 100644 tests/test_dict2css_/test_stylesheet.css diff --git a/dict2css/__init__.py b/dict2css/__init__.py index c51e520..a116436 100644 --- a/dict2css/__init__.py +++ b/dict2css/__init__.py @@ -31,11 +31,11 @@ # stdlib from io import TextIOBase -from typing import IO, Any, Dict, Iterable, List, Mapping, MutableMapping, Sequence, Union, cast +from typing import IO, Any, Dict, Iterable, List, Mapping, MutableMapping, Optional, Sequence, Union # 3rd party -import tinycss2 -import tinycss2.ast +import tinycss2 # type: ignore[import-untyped] +import tinycss2.ast # type: ignore[import-untyped] from domdf_python_tools.paths import PathPlus from domdf_python_tools.typing import PathLike from domdf_python_tools.words import TAB @@ -45,7 +45,7 @@ from dict2css.serializer import CSSSerializer __author__: str = "Dominic Davis-Foster" -__copyright__: str = "2020-2021 Dominic Davis-Foster" +__copyright__: str = "2020-2026 Dominic Davis-Foster" __license__: str = "MIT License" __version__: str = "0.4.0" __email__: str = "dominic@davis-foster.co.uk" @@ -57,10 +57,10 @@ "dump", "loads", "load", - "StyleSheet", - "make_style", ] +# TODO: allow int indent like json.dumps etc. + IMPORTANT = "important" """ The string ``'important'``. @@ -73,7 +73,7 @@ Style = Mapping[str, Property] """ -Type annotation representing a style for :func:`~.make_style` and :func:`~.dumps`. +Type annotation representing a style for :func:`~.dumps` and :func:`~.dump`. The keys are CSS properties. @@ -89,9 +89,11 @@ def dumps( styles: Mapping[str, Union[Style, Mapping]], *, indent: str = TAB, - trailing_semicolon: bool = False, + trailing_semicolon: Optional[bool] = None, indent_closing_brace: bool = False, minify: bool = False, + sort_keys: bool = False, + check_circular: bool = True, ) -> str: r""" Construct a cascading style sheet from a dictionary. @@ -135,6 +137,8 @@ def dumps( :param trailing_semicolon: Whether to add a semicolon to the end of the final property. :param indent_closing_brace: :param minify: Minify the CSS. Overrides all other options. + :param sort_keys: Sort dictionary keys alphabetically. + :param check_circular: Check for circular references. :return: The style sheet as a string. @@ -146,27 +150,11 @@ def dumps( trailing_semicolon=trailing_semicolon, indent_closing_brace=indent_closing_brace, minify=minify, + sort_keys=sort_keys, + check_circular=check_circular, ) - stylesheet: str = '' - - with serializer.use(): - sheet = StyleSheet() - - for selector, style in styles.items(): - if selector.startswith("@media"): - sheet.add_media_styles(selector.split("@media")[1].strip(), cast(Mapping[str, Style], style)) - elif selector.startswith('@'): - raise NotImplementedError("Only @media at-rules are supported at this time.") - else: - sheet.add_style(selector, cast(Style, style)) - - stylesheet = sheet.tostring() - - if not serializer.minify: - stylesheet = stylesheet.replace('}', "}\n") - - return stylesheet + return serializer.encode(styles) def dump( @@ -174,9 +162,11 @@ def dump( fp: Union[PathLike, IO], *, indent: str = TAB, - trailing_semicolon: bool = False, + trailing_semicolon: Optional[bool] = None, indent_closing_brace: bool = False, minify: bool = False, + sort_keys: bool = False, + check_circular: bool = True, ) -> None: r""" Construct a style sheet from a dictionary and write it to ``fp``. @@ -221,6 +211,8 @@ def dump( :param trailing_semicolon: Whether to add a semicolon to the end of the final property. :param indent_closing_brace: :param minify: Minify the CSS. Overrides all other options. + :param sort_keys: Sort dictionary keys alphabetically. + :param check_circular: Check for circular references. .. versionchanged:: 0.2.0 @@ -234,6 +226,8 @@ def dump( indent=indent, trailing_semicolon=trailing_semicolon, indent_closing_brace=indent_closing_brace, + sort_keys=sort_keys, + check_circular=check_circular, minify=minify, ) @@ -279,7 +273,7 @@ def parse_style(style: List[tinycss2.ast.Node]) -> MutableMapping[str, Property] styles_dict[_serialize(rule.prelude)] = parse_style(rule.content) elif isinstance(rule, tinycss2.ast.AtRule): - at_rule_styles = styles_dict[f"@{rule.at_keyword} {_serialize(rule.prelude)}"] = at_rule_styles + at_rule_styles = styles_dict[f"@{rule.at_keyword} {_serialize(rule.prelude)}"] for child in tinycss2.parse_blocks_contents(rule.content, skip_comments=True, skip_whitespace=True): 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] return styles_dict -def _serialize(nodes: Iterable[tinycss2.ast.Node]): +def _serialize(nodes: Iterable[tinycss2.ast.Node]) -> str: return tinycss2.serialize(nodes).strip() @@ -311,93 +305,3 @@ def load(fp: Union[PathLike, IO]) -> MutableMapping[str, MutableMapping[str, Any styles = PathPlus(fp).read_text() return loads(styles) - - -class StyleSheet(css_parser.css.CSSStyleSheet): - r""" - Represents a CSS style sheet. - - .. raw:: latex - - \nopagebreak - - .. autosummary-widths:: 7/16 - - """ - - def __init__(self): - super().__init__(validating=False) - - def add(self, rule: css_parser.css.CSSRule) -> int: - """ - Add the ``rule`` to the style sheet. - - :param rule: - :type rule: :class:`css_parser.css.CSSRule` - """ - - return super().add(rule) - - def add_style( - self, - selector: str, - styles: Style, - ) -> None: - """ - Add a style to the style sheet. - - :param selector: - :param styles: - """ - - self.add(make_style(selector, styles)) - - def add_media_styles( - self, - media_query: str, - styles: Mapping[str, Style], - ) -> None: - """ - Add a set of styles for a media query to the style sheet. - - .. versionadded:: 0.2.0 - - :param media_query: - :param styles: - """ - - media = css_parser.css.CSSMediaRule(media_query) - - for selector, style in styles.items(): - media.add(make_style(selector, style)) - - self.add(media) - - def tostring(self) -> str: - """ - Returns the style sheet as a string. - """ - - return self.cssText.decode("UTF-8") - - -def make_style(selector: str, styles: Style) -> css_parser.css.CSSStyleRule: - """ - Create a CSS Style Rule from a dictionary. - - :param selector: - :param styles: - - :rtype: :class:`css_parser.css.CSSStyleRule` - """ - - style = css_parser.css.CSSStyleDeclaration() - style.validating = False - - for name, properties in styles.items(): - if isinstance(properties, Sequence) and not isinstance(properties, str): - style[name] = tuple(str(x) for x in properties) - else: - style[name] = str(properties) - - return css_parser.css.CSSStyleRule(selectorText=selector, style=style) diff --git a/dict2css/serializer.py b/dict2css/serializer.py index c3414e4..a3fe045 100644 --- a/dict2css/serializer.py +++ b/dict2css/serializer.py @@ -9,6 +9,9 @@ # # Copyright © 2021 Dominic Davis-Foster # +# Adapted from https://github.com/austinyu/ujson5 +# Copyright (c) 2025 Sir Austin +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights @@ -29,16 +32,45 @@ # # stdlib +import re from contextlib import contextmanager -from typing import Iterator +from typing import Any, Callable, Dict, Iterable, Iterator, Mapping, Optional, Union # 3rd party from domdf_python_tools.words import TAB __all__ = ["CSSSerializer"] +#: Python objects that can be serialized to CSS +Serializable = Union[dict, list, tuple, int, float, str, bool] + +#: A callable that takes in an object that is not serializable and returns a serializable object +DefaultInterface = Union[ + Callable[[Any], dict], + Callable[[Any], list], + Callable[[Any], tuple], + Callable[[Any], int], + Callable[[Any], float], + Callable[[Any], str], + Callable[[Any], bool], + Callable[[Any], Serializable] + ] + +ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]') +ESCAPE_DCT = { + '\\': "\\\\", + '"': '\\"', + '\x08': "\\b", + '\x0c': "\\f", + '\n': "\\n", + '\r': "\\r", + '\t': "\\t", + } +for i in range(0x20): + ESCAPE_DCT.setdefault(chr(i), f"\\u{i:04x}") -class CSSSerializer(css_parser.CSSSerializer): + +class CSSSerializer: r""" Serializes a :class:`~.StyleSheet` and its parts. @@ -48,6 +80,8 @@ class CSSSerializer(css_parser.CSSSerializer): :param trailing_semicolon: Whether to add a semicolon to the end of the final property. :param indent_closing_brace: :param minify: Minify the CSS. Overrides all other options. + :param sort_keys: Sort dictionary keys alphabetically. + :param check_circular: Check for circular references. .. autosummary-widths:: 5/16 """ @@ -56,49 +90,287 @@ def __init__( self, *, indent: str = TAB, - trailing_semicolon: bool = False, + trailing_semicolon: Optional[bool] = None, + indent_closing_brace: bool = False, + minify: bool = False, + sort_keys: bool = False, + check_circular: bool = True, # default: Optional[DefaultInterface] = None, + ) -> None: + self._set_options( + indent=indent, + trailing_semicolon=trailing_semicolon, + indent_closing_brace=indent_closing_brace, + minify=minify, + sort_keys=sort_keys, + ) + + if check_circular: + self._markers: Optional[Dict[int, Any]] = {} + else: + self._markers = None + + # TODO self._default: Optional[DefaultInterface] = default + self._default: Optional[DefaultInterface] = None + + def _set_options( + self, + indent: str = TAB, + trailing_semicolon: Optional[bool] = None, indent_closing_brace: bool = False, minify: bool = False, - ): - super().__init__() - self.indent = str(indent) - self.trailing_semicolon = trailing_semicolon - self.indent_closing_brace = indent_closing_brace - self.minify = minify + sort_keys: bool = False, + ) -> None: + + self._sort_keys: bool = sort_keys + self._indent_str: Optional[str] = None if minify else indent + self._key_separator: str = ": " + + if not indent and not minify: + self._item_separator: str = "; " + else: + self._item_separator = ';' + + if minify: + self._trailing_semicolon: bool = True + self._key_separator = ':' + elif trailing_semicolon is None: + self._trailing_semicolon = indent is not None + else: + self._trailing_semicolon = trailing_semicolon + + if indent_closing_brace and indent is not None: + self._indent_closing_brace = True + else: + self._indent_closing_brace = False + + self._minify = minify def reset_style(self) -> None: """ Reset the serializer to its default style. """ - # Reset CSS Parser to defaults - self.prefs.useDefaults() + self._set_options() + + def encode(self, obj: Mapping) -> str: + """ + Return a CSS representation of a Python dictionary. + + :param obj: The Python object to be serialized + + :returns: The CSS string representation of the Python object + """ + + return ''.join(self.iterencode(obj)) + + def iterencode(self, obj: Mapping) -> Iterable[str]: + """ + Encode the given dictionary and yield each part of the CSS string representation. + + :param obj: The Python dictionary to be serialized + + :returns: An iterable of strings representing the CSS serialization of the Python dictionary. + """ + + if not isinstance(obj, Mapping): + raise TypeError(f"Cannot convert {type(obj)} to CSS") + + return self._iterencode_dict(obj, indent_level=-1) + + def default(self, obj: Any) -> Serializable: + """ + Override this method in a subclass to implement custom serialization for objects that are not serializable by default. + + This method should return a serializable object. + If this method is not overridden, the encoder will raise a :exc:`ValueError` when trying to encode an unsupported object. + + :param obj: The object to be serialized that is not supported by default. + + :returns: A serializable object + + :raises: ValueError: If the object cannot be serialized + """ + + if self._default is not None: + return self._default(obj) + raise ValueError(f"Object of type {obj.__class__.__name__} is not JSON serializable") + + def _encode_float(self, obj: float) -> str: + if obj != obj or obj == float("inf") or obj == float("-inf"): + raise ValueError(f"Out of range float values are not allowed: {repr(obj)}") + else: + return repr(obj) + + def _encode_str(self, obj: str) -> str: + + def replace_unicode(match: re.Match) -> str: + return ESCAPE_DCT[match.group(0)] + + return ESCAPE.sub(replace_unicode, obj) + + def _iterencode(self, obj: Any, indent_level: int) -> Iterable[str]: + if isinstance(obj, str): + yield self._encode_str(obj) + elif obj is True: + yield "true" + elif obj is False: + yield "false" + elif isinstance(obj, int): + yield repr(obj) + elif isinstance(obj, float): + yield self._encode_float(obj) + elif isinstance(obj, (list, tuple)): + yield from self._iterencode_list(obj, indent_level) + elif isinstance(obj, dict): + yield from self._iterencode_dict(obj, indent_level) + else: + if self._markers is not None: + marker_id: Optional[int] = id(obj) + if marker_id in self._markers: + raise ValueError("Circular reference detected") + assert marker_id is not None + self._markers[marker_id] = obj + else: + marker_id = None + + obj_user = self.default(obj) + yield from self._iterencode(obj_user, indent_level) - if self.minify: - self.prefs.useMinified() + if self._markers is not None and marker_id is not None: + del self._markers[marker_id] + def _iterencode_list( + self, + obj: Union[list, tuple, None], + indent_level: int, + ) -> Iterable[str]: + + # this package + from dict2css import IMPORTANT + + if not obj: + yield "[]" + return + + if self._markers is not None: + marker_id: Optional[int] = id(obj) + if marker_id in self._markers: + raise ValueError("Circular reference detected") + assert marker_id is not None + self._markers[marker_id] = obj else: - # Formatting preferences - self.prefs.omitLastSemicolon = not self.trailing_semicolon - self.prefs.indentClosingBrace = self.indent_closing_brace - self.prefs.indent = self.indent + marker_id = None + + first: bool = True + for value in obj: + if first: + yield '' + first = False + elif not self._minify: + yield ' ' + if isinstance(value, (list, tuple)): + yield from self._iterencode_list(value, indent_level) + elif isinstance(value, dict): + yield from self._iterencode_dict(value, indent_level) + elif value == IMPORTANT: + yield "!important" + else: + yield from self._iterencode(value, indent_level) + + if self._markers is not None and marker_id is not None: + del self._markers[marker_id] + + def _iterencode_dict( + self, + obj: Mapping[str, Any], + indent_level: int, + ) -> Iterable[str]: + if not obj: + yield "{}" + return + + if self._markers is not None: + marker_id: Optional[int] = id(obj) + if marker_id in self._markers: + raise ValueError("Circular reference detected") + assert marker_id is not None + self._markers[marker_id] = obj + else: + marker_id = None + + if indent_level >= 0: + yield '{' + + minify_indent = False + if self._minify or not self._indent_str: + # No indent + newline_indent: Optional[str] = None + + if indent_level == -1: + indent_level += 1 + minify_indent = True + + else: + indent_level += 1 + newline_indent = '\n' + self._indent_str * indent_level + assert newline_indent is not None + yield newline_indent + + first = True + if self._sort_keys: + items: Any = sorted(obj.items()) + else: + items = obj.items() + + total_items: int = len(items) + for idx, (key, value) in enumerate(items): + if not isinstance(key, str): + raise ValueError(f"keys must be strings, not {key.__class__.__name__}") + + if first: + first = False + elif newline_indent is not None: + yield newline_indent # we do not need to yield anything if indent == 0 + yield self._encode_str(key) + + if isinstance(value, dict): + if not self._minify: + yield ' ' + + yield from self._iterencode_dict(value, indent_level) + else: + yield self._key_separator + + if isinstance(value, (list, tuple)): + yield from self._iterencode_list(value, indent_level) + else: + yield from self._iterencode(value, indent_level) + + if idx != total_items - 1 or self._trailing_semicolon: + yield self._item_separator + + if self._indent_str: + indent_level -= 1 + yield '\n' + self._indent_str * indent_level + elif minify_indent: + indent_level -= 1 + + if indent_level >= 0: + if self._indent_closing_brace: + assert self._indent_str is not None + yield self._indent_str + '}' + else: + yield '}' + + if self._markers is not None and marker_id is not None: + del self._markers[marker_id] + + # TODO: deprecate @contextmanager def use(self) -> Iterator: """ - Contextmanager to use this serializer for the scope of the ``with`` block. + No-op. Deprecated. """ - # if css_parser.ser is self: - # yield - # return - # - current_serializer = css_parser.ser - self.reset_style() - - try: - css_parser.ser = self - yield - - finally: - css_parser.ser = current_serializer + yield diff --git a/tests/test_dict2css.py b/tests/test_dict2css.py index 3670135..3390478 100644 --- a/tests/test_dict2css.py +++ b/tests/test_dict2css.py @@ -5,33 +5,11 @@ import pytest from coincidence.regressions import AdvancedDataRegressionFixture, AdvancedFileRegressionFixture from domdf_python_tools.paths import PathPlus -from domdf_python_tools.stringlist import StringList from domdf_python_tools.words import TAB # this package -from dict2css import IMPORTANT, Style, StyleSheet, dump, dumps, load, loads, make_style -from dict2css.helpers import em, px, rem -from dict2css.serializer import CSSSerializer - - -def test_stylesheet(advanced_file_regression: AdvancedFileRegressionFixture): - serializer = CSSSerializer(indent=" ", trailing_semicolon=True) - - with serializer.use(): - sheet = StyleSheet() - - # Body width - sheet.add_style(".wy-nav-content", {"max-width": (px(1200), IMPORTANT)}) - - # Spacing between list items - sheet.add_style("li p:last-child", {"margin-bottom": (px(12), IMPORTANT)}) - - # Smooth scrolling between sections - sheet.add_style("html", {"scroll-behavior": "smooth"}) - - stylesheet = sheet.tostring().replace('}', "}\n") - - advanced_file_regression.check(stylesheet, extension=".css") +from dict2css import IMPORTANT, Style, dump, dumps, load, loads +from dict2css.helpers import em, rem @pytest.mark.parametrize("trailing_semicolon", [True, False]) @@ -85,25 +63,6 @@ def test_dumps( advanced_file_regression.check_file(output_file) -def test_make_style(): - style: cssutils.css.CSSStyleRule = make_style("li p:last-child", {"max-width": (rem(1200), IMPORTANT)}) - assert str(style.selectorText) == "li p:last-child" - assert StringList(style.cssText) == [ - "li p:last-child {", - " max-width: 1200rem !important", - " }", - ] - - serializer = CSSSerializer(trailing_semicolon=True) - - with serializer.use(): - assert StringList(style.cssText) == [ - "li p:last-child {", - "\tmax-width: 1200rem !important;", - '}', - ] - - def test_dump_minify(advanced_file_regression: AdvancedFileRegressionFixture, tmp_pathplus: PathPlus): stylesheet: Dict[str, Style] = { ".wy-nav-content": {"max-width": (rem(1200), IMPORTANT)}, diff --git a/tests/test_dict2css_/test_stylesheet.css b/tests/test_dict2css_/test_stylesheet.css deleted file mode 100644 index 2fe26dc..0000000 --- a/tests/test_dict2css_/test_stylesheet.css +++ /dev/null @@ -1,11 +0,0 @@ -.wy-nav-content { - max-width: 1200px !important; -} - -li p:last-child { - margin-bottom: 12px !important; -} - -html { - scroll-behavior: smooth; -} From fc1755829c76a0c6a01fff53ac61d5b81711aeb1 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Thu, 7 May 2026 18:27:01 +0100 Subject: [PATCH 03/13] Fixes --- dict2css/__init__.py | 2 +- dict2css/serializer.py | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/dict2css/__init__.py b/dict2css/__init__.py index a116436..3746d24 100644 --- a/dict2css/__init__.py +++ b/dict2css/__init__.py @@ -273,7 +273,7 @@ def parse_style(style: List[tinycss2.ast.Node]) -> MutableMapping[str, Property] styles_dict[_serialize(rule.prelude)] = parse_style(rule.content) elif isinstance(rule, tinycss2.ast.AtRule): - at_rule_styles = styles_dict[f"@{rule.at_keyword} {_serialize(rule.prelude)}"] + at_rule_styles = styles_dict[f"@{rule.at_keyword} {_serialize(rule.prelude)}"] = {} for child in tinycss2.parse_blocks_contents(rule.content, skip_comments=True, skip_whitespace=True): at_rule_styles[_serialize(child.prelude)] = parse_style(child.content) diff --git a/dict2css/serializer.py b/dict2css/serializer.py index a3fe045..08f07f2 100644 --- a/dict2css/serializer.py +++ b/dict2css/serializer.py @@ -123,7 +123,11 @@ def _set_options( self._sort_keys: bool = sort_keys self._indent_str: Optional[str] = None if minify else indent - self._key_separator: str = ": " + + if minify: + self._key_separator: str = ':' + else: + self._key_separator = ": " if not indent and not minify: self._item_separator: str = "; " @@ -131,8 +135,7 @@ def _set_options( self._item_separator = ';' if minify: - self._trailing_semicolon: bool = True - self._key_separator = ':' + self._trailing_semicolon = False elif trailing_semicolon is None: self._trailing_semicolon = indent is not None else: @@ -266,7 +269,7 @@ def _iterencode_list( if first: yield '' first = False - elif not self._minify: + else: yield ' ' if isinstance(value, (list, tuple)): @@ -315,7 +318,9 @@ def _iterencode_dict( indent_level += 1 newline_indent = '\n' + self._indent_str * indent_level assert newline_indent is not None - yield newline_indent + + if indent_level > 0: + yield newline_indent first = True if self._sort_keys: @@ -359,9 +364,12 @@ def _iterencode_dict( if indent_level >= 0: if self._indent_closing_brace: assert self._indent_str is not None - yield self._indent_str + '}' - else: - yield '}' + yield self._indent_str + + yield '}' + + if not self._minify: + yield '\n' if self._markers is not None and marker_id is not None: del self._markers[marker_id] From adc6b9ecf71f3114dc3ed7f4df211d1fabbe2926 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Thu, 7 May 2026 22:46:58 +0100 Subject: [PATCH 04/13] Increase test coverage and minor fixes --- dict2css/serializer.py | 8 +- tests/test_dict2css.py | 83 +++++++++++++++++-- ...emicolon_not_sort_keys_check_circular_.css | 3 + ...olon_not_sort_keys_not_check_circular_.css | 3 + ...ng_semicolon_sort_keys_check_circular_.css | 3 + ...emicolon_sort_keys_not_check_circular_.css | 3 + ...emicolon_not_sort_keys_check_circular_.css | 3 + ...olon_not_sort_keys_not_check_circular_.css | 3 + ...ng_semicolon_sort_keys_check_circular_.css | 3 + ...emicolon_sort_keys_not_check_circular_.css | 3 + ...emicolon_not_sort_keys_check_circular_.css | 3 + ...olon_not_sort_keys_not_check_circular_.css | 3 + ...ng_semicolon_sort_keys_check_circular_.css | 3 + ...emicolon_sort_keys_not_check_circular_.css | 3 + ...emicolon_not_sort_keys_check_circular_.css | 3 + ...olon_not_sort_keys_not_check_circular_.css | 3 + ...ng_semicolon_sort_keys_check_circular_.css | 3 + ...emicolon_sort_keys_not_check_circular_.css | 3 + ...emicolon_not_sort_keys_check_circular_.css | 16 ++++ ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ ...micolon_not_sort_keys_check_circular_.css} | 4 + ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ ...emicolon_not_sort_keys_check_circular_.css | 16 ++++ ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ ...micolon_not_sort_keys_check_circular_.css} | 4 + ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ ...emicolon_not_sort_keys_check_circular_.css | 16 ++++ ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ ...micolon_not_sort_keys_check_circular_.css} | 4 + ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ ...emicolon_not_sort_keys_check_circular_.css | 16 ++++ ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ ...micolon_not_sort_keys_check_circular_.css} | 4 + ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ .../test_dumps____False_False_.css | 12 --- .../test_dumps____True_False_.css | 12 --- .../test_dumps______False_False_.css | 12 --- .../test_dumps______True_False_.css | 12 --- .../test_dumps__t_False_False_.css | 12 --- .../test_dumps__t_True_False_.css | 12 --- ...emicolon_not_sort_keys_check_circular_.css | 16 ++++ ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ ...micolon_not_sort_keys_check_circular_.css} | 4 + ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ ...emicolon_not_sort_keys_check_circular_.css | 16 ++++ ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ ...micolon_not_sort_keys_check_circular_.css} | 4 + ...olon_not_sort_keys_not_check_circular_.css | 16 ++++ ...ng_semicolon_sort_keys_check_circular_.css | 16 ++++ ...emicolon_sort_keys_not_check_circular_.css | 16 ++++ 72 files changed, 826 insertions(+), 81 deletions(-) create mode 100644 tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css rename tests/test_dict2css_/{test_dumps____True_True_.css => test_dumps_2_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css} (66%) create mode 100644 tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css rename tests/test_dict2css_/{test_dumps____False_True_.css => test_dumps_2_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css} (65%) create mode 100644 tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css rename tests/test_dict2css_/{test_dumps______True_True_.css => test_dumps_4_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css} (66%) create mode 100644 tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css rename tests/test_dict2css_/{test_dumps______False_True_.css => test_dumps_4_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css} (64%) create mode 100644 tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css delete mode 100644 tests/test_dict2css_/test_dumps____False_False_.css delete mode 100644 tests/test_dict2css_/test_dumps____True_False_.css delete mode 100644 tests/test_dict2css_/test_dumps______False_False_.css delete mode 100644 tests/test_dict2css_/test_dumps______True_False_.css delete mode 100644 tests/test_dict2css_/test_dumps__t_False_False_.css delete mode 100644 tests/test_dict2css_/test_dumps__t_True_False_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css rename tests/test_dict2css_/{test_dumps__t_True_True_.css => test_dumps_tab_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css} (66%) create mode 100644 tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css rename tests/test_dict2css_/{test_dumps__t_False_True_.css => test_dumps_tab_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css} (66%) create mode 100644 tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css create mode 100644 tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css diff --git a/dict2css/serializer.py b/dict2css/serializer.py index 08f07f2..70c5353 100644 --- a/dict2css/serializer.py +++ b/dict2css/serializer.py @@ -194,9 +194,10 @@ def default(self, obj: Any) -> Serializable: :raises: ValueError: If the object cannot be serialized """ - if self._default is not None: + if self._default is not None: # pragma no cover # TODO return self._default(obj) - raise ValueError(f"Object of type {obj.__class__.__name__} is not JSON serializable") + + raise ValueError(f"Object of type {obj.__class__.__name__} cannot be represented in CSS") def _encode_float(self, obj: float) -> str: if obj != obj or obj == float("inf") or obj == float("-inf"): @@ -252,8 +253,7 @@ def _iterencode_list( from dict2css import IMPORTANT if not obj: - yield "[]" - return + raise ValueError("Property cannot be empty") if self._markers is not None: marker_id: Optional[int] = id(obj) diff --git a/tests/test_dict2css.py b/tests/test_dict2css.py index 3390478..24960cb 100644 --- a/tests/test_dict2css.py +++ b/tests/test_dict2css.py @@ -1,4 +1,5 @@ # stdlib +from ipaddress import IPv4Address from typing import Dict, Mapping, MutableMapping # 3rd party @@ -9,33 +10,105 @@ # this package from dict2css import IMPORTANT, Style, dump, dumps, load, loads -from dict2css.helpers import em, rem +from dict2css.helpers import em, px, rem -@pytest.mark.parametrize("trailing_semicolon", [True, False]) -@pytest.mark.parametrize("indent_closing_brace", [True, False]) -@pytest.mark.parametrize("indent", [TAB, " ", " "]) +def boolean_option(name: str, id: str): # noqa: A002,MAN002 # pylint: disable=redefined-builtin + return pytest.mark.parametrize( + name, + [ + pytest.param(True, id=id), + pytest.param(False, id=f"not {id}"), + ], + ) + + +def test_dumps_not_mapping(): + with pytest.raises(TypeError, match="Cannot convert .* to CSS"): + dumps([1, 2, 3]) + + with pytest.raises(TypeError, match="Cannot convert .* to CSS"): + dumps((1, 2, 3)) + + with pytest.raises(TypeError, match="Cannot convert .* to CSS"): + dumps("ABC") + + with pytest.raises(TypeError, match="Cannot convert .* to CSS"): + dumps(123) + + with pytest.raises(TypeError, match="Cannot convert .* to CSS"): + dumps(123.456) + + with pytest.raises(TypeError, match="Cannot convert .* to CSS"): + dumps(True) + + with pytest.raises(TypeError, match="Cannot convert .* to CSS"): + dumps(None) + + +def test_dumps_unknown_type(): + with pytest.raises(ValueError, match="Object of type .* cannot be represented in CSS"): + dumps({"the_key": None}) + + with pytest.raises(ValueError, match="Object of type .* cannot be represented in CSS"): + dumps({"the_key": IPv4Address("127.0.0.1")}) + + +def test_dumps_bad_floats(): + with pytest.raises(ValueError, match="Out of range float values are not allowed:"): + dumps({"the_key": float("inf")}) + + with pytest.raises(ValueError, match="Out of range float values are not allowed:"): + dumps({"the_key": float("-inf")}) + + with pytest.raises(ValueError, match="Out of range float values are not allowed:"): + dumps({"the_key": float("nan")}) + + +@boolean_option("check_circular", "check_circular") +@boolean_option("sort_keys", "sort_keys") +@boolean_option("trailing_semicolon", "trailing_semicolon") +@boolean_option("indent_closing_brace", "indent_closing_brace") +@pytest.mark.parametrize( + "indent", + [ + pytest.param(TAB, id="tab"), + pytest.param(" ", id='2'), + pytest.param(" ", id='4'), + pytest.param('', id='0'), + ], + ) def test_dumps( advanced_file_regression: AdvancedFileRegressionFixture, trailing_semicolon: bool, indent_closing_brace: bool, indent: str, + check_circular: bool, + sort_keys: bool, tmp_pathplus: PathPlus, ): stylesheet: Dict[str, Style] = { - ".wy-nav-content": {"max-width": (rem(1200), IMPORTANT)}, + ".wy-nav-content": {"max-width": (rem(1200), IMPORTANT), "z-index": 999}, "li p:last-child": { "margin-bottom": (em(12), IMPORTANT), "margin-top": em(6), + "font-size": px(14), + "line-height": 1.5, + "font-weight": (800, IMPORTANT), }, "html": {"scroll-behavior": "smooth"}, } + # TODO + sort_keys = False + css = dumps( stylesheet, indent=indent, trailing_semicolon=trailing_semicolon, indent_closing_brace=indent_closing_brace, + check_circular=check_circular, + sort_keys=sort_keys, ) advanced_file_regression.check(css, extension=".css") diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css new file mode 100644 index 0000000..187f96e --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999} +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} +html {scroll-behavior: smooth} diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..187f96e --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999} +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} +html {scroll-behavior: smooth} diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..187f96e --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999} +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} +html {scroll-behavior: smooth} diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..187f96e --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999} +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} +html {scroll-behavior: smooth} diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css new file mode 100644 index 0000000..ff02894 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999; } +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } +html {scroll-behavior: smooth; } diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..ff02894 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999; } +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } +html {scroll-behavior: smooth; } diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..ff02894 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999; } +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } +html {scroll-behavior: smooth; } diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..ff02894 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999; } +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } +html {scroll-behavior: smooth; } diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css new file mode 100644 index 0000000..187f96e --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999} +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} +html {scroll-behavior: smooth} diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..187f96e --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999} +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} +html {scroll-behavior: smooth} diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..187f96e --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999} +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} +html {scroll-behavior: smooth} diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..187f96e --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999} +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} +html {scroll-behavior: smooth} diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css new file mode 100644 index 0000000..ff02894 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999; } +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } +html {scroll-behavior: smooth; } diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..ff02894 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999; } +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } +html {scroll-behavior: smooth; } diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..ff02894 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999; } +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } +html {scroll-behavior: smooth; } diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..ff02894 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,3 @@ +.wy-nav-content {max-width: 1200rem !important; z-index: 999; } +li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } +html {scroll-behavior: smooth; } diff --git a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css new file mode 100644 index 0000000..6afd4a9 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..6afd4a9 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..6afd4a9 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..6afd4a9 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps____True_True_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css similarity index 66% rename from tests/test_dict2css_/test_dumps____True_True_.css rename to tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css index 639a7ab..905e248 100644 --- a/tests/test_dict2css_/test_dumps____True_True_.css +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css @@ -1,10 +1,14 @@ .wy-nav-content { max-width: 1200rem !important; + z-index: 999; } li p:last-child { margin-bottom: 12em !important; margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; } html { diff --git a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..905e248 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; + } + +html { + scroll-behavior: smooth; + } diff --git a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..905e248 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; + } + +html { + scroll-behavior: smooth; + } diff --git a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..905e248 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; + } + +html { + scroll-behavior: smooth; + } diff --git a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css new file mode 100644 index 0000000..5c14ff5 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..5c14ff5 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..5c14ff5 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..5c14ff5 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps____False_True_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css similarity index 65% rename from tests/test_dict2css_/test_dumps____False_True_.css rename to tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css index 1cf3c1d..2dc8aba 100644 --- a/tests/test_dict2css_/test_dumps____False_True_.css +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css @@ -1,10 +1,14 @@ .wy-nav-content { max-width: 1200rem !important; + z-index: 999; } li p:last-child { margin-bottom: 12em !important; margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; } html { diff --git a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..2dc8aba --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; +} + +html { + scroll-behavior: smooth; +} diff --git a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..2dc8aba --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; +} + +html { + scroll-behavior: smooth; +} diff --git a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..2dc8aba --- /dev/null +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; +} + +html { + scroll-behavior: smooth; +} diff --git a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css new file mode 100644 index 0000000..ffbd0e7 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..ffbd0e7 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..ffbd0e7 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..ffbd0e7 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps______True_True_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css similarity index 66% rename from tests/test_dict2css_/test_dumps______True_True_.css rename to tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css index 056d939..67a5fe0 100644 --- a/tests/test_dict2css_/test_dumps______True_True_.css +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css @@ -1,10 +1,14 @@ .wy-nav-content { max-width: 1200rem !important; + z-index: 999; } li p:last-child { margin-bottom: 12em !important; margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; } html { diff --git a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..67a5fe0 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; + } + +html { + scroll-behavior: smooth; + } diff --git a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..67a5fe0 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; + } + +html { + scroll-behavior: smooth; + } diff --git a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..67a5fe0 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; + } + +html { + scroll-behavior: smooth; + } diff --git a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css new file mode 100644 index 0000000..76b9976 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..76b9976 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..76b9976 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..76b9976 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps______False_True_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css similarity index 64% rename from tests/test_dict2css_/test_dumps______False_True_.css rename to tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css index d84370c..19c3f36 100644 --- a/tests/test_dict2css_/test_dumps______False_True_.css +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css @@ -1,10 +1,14 @@ .wy-nav-content { max-width: 1200rem !important; + z-index: 999; } li p:last-child { margin-bottom: 12em !important; margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; } html { diff --git a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..19c3f36 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; +} + +html { + scroll-behavior: smooth; +} diff --git a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..19c3f36 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; +} + +html { + scroll-behavior: smooth; +} diff --git a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..19c3f36 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; +} + +html { + scroll-behavior: smooth; +} diff --git a/tests/test_dict2css_/test_dumps____False_False_.css b/tests/test_dict2css_/test_dumps____False_False_.css deleted file mode 100644 index be1415b..0000000 --- a/tests/test_dict2css_/test_dumps____False_False_.css +++ /dev/null @@ -1,12 +0,0 @@ -.wy-nav-content { - max-width: 1200rem !important -} - -li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em -} - -html { - scroll-behavior: smooth -} diff --git a/tests/test_dict2css_/test_dumps____True_False_.css b/tests/test_dict2css_/test_dumps____True_False_.css deleted file mode 100644 index bac0891..0000000 --- a/tests/test_dict2css_/test_dumps____True_False_.css +++ /dev/null @@ -1,12 +0,0 @@ -.wy-nav-content { - max-width: 1200rem !important - } - -li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em - } - -html { - scroll-behavior: smooth - } diff --git a/tests/test_dict2css_/test_dumps______False_False_.css b/tests/test_dict2css_/test_dumps______False_False_.css deleted file mode 100644 index 43fbdb2..0000000 --- a/tests/test_dict2css_/test_dumps______False_False_.css +++ /dev/null @@ -1,12 +0,0 @@ -.wy-nav-content { - max-width: 1200rem !important -} - -li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em -} - -html { - scroll-behavior: smooth -} diff --git a/tests/test_dict2css_/test_dumps______True_False_.css b/tests/test_dict2css_/test_dumps______True_False_.css deleted file mode 100644 index cce7634..0000000 --- a/tests/test_dict2css_/test_dumps______True_False_.css +++ /dev/null @@ -1,12 +0,0 @@ -.wy-nav-content { - max-width: 1200rem !important - } - -li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em - } - -html { - scroll-behavior: smooth - } diff --git a/tests/test_dict2css_/test_dumps__t_False_False_.css b/tests/test_dict2css_/test_dumps__t_False_False_.css deleted file mode 100644 index fe9a067..0000000 --- a/tests/test_dict2css_/test_dumps__t_False_False_.css +++ /dev/null @@ -1,12 +0,0 @@ -.wy-nav-content { - max-width: 1200rem !important -} - -li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em -} - -html { - scroll-behavior: smooth -} diff --git a/tests/test_dict2css_/test_dumps__t_True_False_.css b/tests/test_dict2css_/test_dumps__t_True_False_.css deleted file mode 100644 index bbe19ce..0000000 --- a/tests/test_dict2css_/test_dumps__t_True_False_.css +++ /dev/null @@ -1,12 +0,0 @@ -.wy-nav-content { - max-width: 1200rem !important - } - -li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em - } - -html { - scroll-behavior: smooth - } diff --git a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css new file mode 100644 index 0000000..fa2263f --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..fa2263f --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..fa2263f --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..fa2263f --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important + } + +html { + scroll-behavior: smooth + } diff --git a/tests/test_dict2css_/test_dumps__t_True_True_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css similarity index 66% rename from tests/test_dict2css_/test_dumps__t_True_True_.css rename to tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css index 3c47fb8..60d3152 100644 --- a/tests/test_dict2css_/test_dumps__t_True_True_.css +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css @@ -1,10 +1,14 @@ .wy-nav-content { max-width: 1200rem !important; + z-index: 999; } li p:last-child { margin-bottom: 12em !important; margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; } html { diff --git a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..60d3152 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; + } + +html { + scroll-behavior: smooth; + } diff --git a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..60d3152 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; + } + +html { + scroll-behavior: smooth; + } diff --git a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..60d3152 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; + } + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; + } + +html { + scroll-behavior: smooth; + } diff --git a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css new file mode 100644 index 0000000..ba6d847 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..ba6d847 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..ba6d847 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..ba6d847 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999 +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important +} + +html { + scroll-behavior: smooth +} diff --git a/tests/test_dict2css_/test_dumps__t_False_True_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css similarity index 66% rename from tests/test_dict2css_/test_dumps__t_False_True_.css rename to tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css index 6b4100e..d5a9c42 100644 --- a/tests/test_dict2css_/test_dumps__t_False_True_.css +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_not_sort_keys_check_circular_.css @@ -1,10 +1,14 @@ .wy-nav-content { max-width: 1200rem !important; + z-index: 999; } li p:last-child { margin-bottom: 12em !important; margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; } html { diff --git a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..d5a9c42 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_not_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; +} + +html { + scroll-behavior: smooth; +} diff --git a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css new file mode 100644 index 0000000..d5a9c42 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; +} + +html { + scroll-behavior: smooth; +} diff --git a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css new file mode 100644 index 0000000..d5a9c42 --- /dev/null +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -0,0 +1,16 @@ +.wy-nav-content { + max-width: 1200rem !important; + z-index: 999; +} + +li p:last-child { + margin-bottom: 12em !important; + margin-top: 6em; + font-size: 14px; + line-height: 1.5; + font-weight: 800 !important; +} + +html { + scroll-behavior: smooth; +} From 01cd5c69912a077ce986cf384ea97b401ae2832d Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Thu, 7 May 2026 23:23:23 +0100 Subject: [PATCH 05/13] Increase test coverage and minor fixes --- dict2css/__init__.py | 7 ++++++- dict2css/serializer.py | 31 +++++++++++++++---------------- tests/test_dict2css.py | 42 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/dict2css/__init__.py b/dict2css/__init__.py index 3746d24..374deb4 100644 --- a/dict2css/__init__.py +++ b/dict2css/__init__.py @@ -154,7 +154,12 @@ def dumps( check_circular=check_circular, ) - return serializer.encode(styles) + css = serializer.encode(styles).rstrip() + + if css: + return css + '\n' + else: + return '' def dump( diff --git a/dict2css/serializer.py b/dict2css/serializer.py index 70c5353..bedef56 100644 --- a/dict2css/serializer.py +++ b/dict2css/serializer.py @@ -148,7 +148,8 @@ def _set_options( self._minify = minify - def reset_style(self) -> None: + # TODO: deprecate + def reset_style(self) -> None: # pragma: no cover """ Reset the serializer to its default style. """ @@ -207,7 +208,7 @@ def _encode_float(self, obj: float) -> str: def _encode_str(self, obj: str) -> str: - def replace_unicode(match: re.Match) -> str: + def replace_unicode(match: re.Match) -> str: # pragma: no cover # TODO return ESCAPE_DCT[match.group(0)] return ESCAPE.sub(replace_unicode, obj) @@ -223,14 +224,14 @@ def _iterencode(self, obj: Any, indent_level: int) -> Iterable[str]: yield repr(obj) elif isinstance(obj, float): yield self._encode_float(obj) - elif isinstance(obj, (list, tuple)): + elif isinstance(obj, (list, tuple)): # pragma: no cover # TODO (needs default exposed) yield from self._iterencode_list(obj, indent_level) - elif isinstance(obj, dict): + elif isinstance(obj, dict): # pragma: no cover # TODO (needs default exposed) yield from self._iterencode_dict(obj, indent_level) else: if self._markers is not None: marker_id: Optional[int] = id(obj) - if marker_id in self._markers: + if marker_id in self._markers: # pragma: no cover # TODO (super edge case) raise ValueError("Circular reference detected") assert marker_id is not None self._markers[marker_id] = obj @@ -238,9 +239,9 @@ def _iterencode(self, obj: Any, indent_level: int) -> Iterable[str]: marker_id = None obj_user = self.default(obj) - yield from self._iterencode(obj_user, indent_level) + yield from self._iterencode(obj_user, indent_level) # pragma: no cover # TODO (needs default exposed) - if self._markers is not None and marker_id is not None: + if self._markers is not None and marker_id is not None: # pragma: no cover # TODO (needs default exposed) del self._markers[marker_id] def _iterencode_list( @@ -257,7 +258,7 @@ def _iterencode_list( if self._markers is not None: marker_id: Optional[int] = id(obj) - if marker_id in self._markers: + if marker_id in self._markers: # pragma: no cover # TODO (super edge case) raise ValueError("Circular reference detected") assert marker_id is not None self._markers[marker_id] = obj @@ -272,11 +273,7 @@ def _iterencode_list( else: yield ' ' - if isinstance(value, (list, tuple)): - yield from self._iterencode_list(value, indent_level) - elif isinstance(value, dict): - yield from self._iterencode_dict(value, indent_level) - elif value == IMPORTANT: + if value == IMPORTANT: yield "!important" else: yield from self._iterencode(value, indent_level) @@ -290,7 +287,9 @@ def _iterencode_dict( indent_level: int, ) -> Iterable[str]: if not obj: - yield "{}" + if indent_level >= 0: + yield "{}" + return if self._markers is not None: @@ -331,7 +330,7 @@ def _iterencode_dict( total_items: int = len(items) for idx, (key, value) in enumerate(items): if not isinstance(key, str): - raise ValueError(f"keys must be strings, not {key.__class__.__name__}") + raise TypeError(f"keys must be strings, not {key.__class__.__name__}") if first: first = False @@ -376,7 +375,7 @@ def _iterencode_dict( # TODO: deprecate @contextmanager - def use(self) -> Iterator: + def use(self) -> Iterator: # pragma: no cover """ No-op. Deprecated. """ diff --git a/tests/test_dict2css.py b/tests/test_dict2css.py index 24960cb..3aeac1e 100644 --- a/tests/test_dict2css.py +++ b/tests/test_dict2css.py @@ -46,12 +46,13 @@ def test_dumps_not_mapping(): dumps(None) -def test_dumps_unknown_type(): +@boolean_option("check_circular", "check_circular") +def test_dumps_unknown_type(check_circular: bool): with pytest.raises(ValueError, match="Object of type .* cannot be represented in CSS"): - dumps({"the_key": None}) + dumps({"the_key": None}, check_circular=check_circular) with pytest.raises(ValueError, match="Object of type .* cannot be represented in CSS"): - dumps({"the_key": IPv4Address("127.0.0.1")}) + dumps({"the_key": IPv4Address("127.0.0.1")}, check_circular=check_circular) def test_dumps_bad_floats(): @@ -223,3 +224,38 @@ def test_loads(advanced_data_regression: AdvancedDataRegressionFixture, tmp_path with style_file.open() as fp: assert load(fp) == stylesheet + + +def test_loads_bad_syntax(): + style = [ + ".wy-nav-content {", + " max-width", + " }", + ] + + with pytest.raises(ValueError, match=""): + loads('\n'.join(style)) + + +def test_circular_references(): + css = { + 'a': {'b': 'c'}, + } + css['d'] = css + + with pytest.raises(ValueError, match="Circular reference detected"): + dumps(css) + + +def test_edge_cases(): + with pytest.raises(TypeError, match="keys must be strings, not"): + dumps({1234: {'a': 'b'}}) + with pytest.raises(ValueError, match="Property cannot be empty"): + dumps({'a': {'b': ()}}) + + assert dumps({}) == '' + assert dumps({'a': {}}) == "a {}\n" + assert dumps({'a': {'b': True}}, indent='') == "a {b: true; }\n" + assert dumps({'a': {'b': False}}, indent='') == "a {b: false; }\n" + + assert dumps({'a': {'b': ('c', ('d', ))}}) == 'a {\n\tb: c d;\n}\n' From 6bfa5b64a7576499c4a0e0ecf65ab2c96235024a Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Thu, 7 May 2026 23:29:28 +0100 Subject: [PATCH 06/13] Fix sort_keys test --- tests/test_dict2css.py | 7 ++++--- ...railing_semicolon_sort_keys_check_circular_.css | 2 +- ...ing_semicolon_sort_keys_not_check_circular_.css | 2 +- ...railing_semicolon_sort_keys_check_circular_.css | 2 +- ...ing_semicolon_sort_keys_not_check_circular_.css | 2 +- ...railing_semicolon_sort_keys_check_circular_.css | 2 +- ...ing_semicolon_sort_keys_not_check_circular_.css | 2 +- ...railing_semicolon_sort_keys_check_circular_.css | 2 +- ...ing_semicolon_sort_keys_not_check_circular_.css | 2 +- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- ...railing_semicolon_sort_keys_check_circular_.css | 14 +++++++------- ...ing_semicolon_sort_keys_not_check_circular_.css | 14 +++++++------- 33 files changed, 180 insertions(+), 179 deletions(-) diff --git a/tests/test_dict2css.py b/tests/test_dict2css.py index 3aeac1e..223a30d 100644 --- a/tests/test_dict2css.py +++ b/tests/test_dict2css.py @@ -100,9 +100,6 @@ def test_dumps( "html": {"scroll-behavior": "smooth"}, } - # TODO - sort_keys = False - css = dumps( stylesheet, indent=indent, @@ -122,6 +119,8 @@ def test_dumps( indent=indent, trailing_semicolon=trailing_semicolon, indent_closing_brace=indent_closing_brace, + check_circular=check_circular, + sort_keys=sort_keys, ) advanced_file_regression.check_file(output_file) @@ -132,6 +131,8 @@ def test_dumps( indent=indent, trailing_semicolon=trailing_semicolon, indent_closing_brace=indent_closing_brace, + check_circular=check_circular, + sort_keys=sort_keys, ) advanced_file_regression.check_file(output_file) diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css index 187f96e..b71a82a 100644 --- a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -1,3 +1,3 @@ .wy-nav-content {max-width: 1200rem !important; z-index: 999} -li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} html {scroll-behavior: smooth} +li p:last-child {font-size: 14px; font-weight: 800 !important; line-height: 1.5; margin-bottom: 12em !important; margin-top: 6em} diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css index 187f96e..b71a82a 100644 --- a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -1,3 +1,3 @@ .wy-nav-content {max-width: 1200rem !important; z-index: 999} -li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} html {scroll-behavior: smooth} +li p:last-child {font-size: 14px; font-weight: 800 !important; line-height: 1.5; margin-bottom: 12em !important; margin-top: 6em} diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css index ff02894..7c02e55 100644 --- a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -1,3 +1,3 @@ .wy-nav-content {max-width: 1200rem !important; z-index: 999; } -li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } html {scroll-behavior: smooth; } +li p:last-child {font-size: 14px; font-weight: 800 !important; line-height: 1.5; margin-bottom: 12em !important; margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css index ff02894..7c02e55 100644 --- a/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_0_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -1,3 +1,3 @@ .wy-nav-content {max-width: 1200rem !important; z-index: 999; } -li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } html {scroll-behavior: smooth; } +li p:last-child {font-size: 14px; font-weight: 800 !important; line-height: 1.5; margin-bottom: 12em !important; margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css index 187f96e..b71a82a 100644 --- a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -1,3 +1,3 @@ .wy-nav-content {max-width: 1200rem !important; z-index: 999} -li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} html {scroll-behavior: smooth} +li p:last-child {font-size: 14px; font-weight: 800 !important; line-height: 1.5; margin-bottom: 12em !important; margin-top: 6em} diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css index 187f96e..b71a82a 100644 --- a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -1,3 +1,3 @@ .wy-nav-content {max-width: 1200rem !important; z-index: 999} -li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important} html {scroll-behavior: smooth} +li p:last-child {font-size: 14px; font-weight: 800 !important; line-height: 1.5; margin-bottom: 12em !important; margin-top: 6em} diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css index ff02894..7c02e55 100644 --- a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -1,3 +1,3 @@ .wy-nav-content {max-width: 1200rem !important; z-index: 999; } -li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } html {scroll-behavior: smooth; } +li p:last-child {font-size: 14px; font-weight: 800 !important; line-height: 1.5; margin-bottom: 12em !important; margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css index ff02894..7c02e55 100644 --- a/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_0_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -1,3 +1,3 @@ .wy-nav-content {max-width: 1200rem !important; z-index: 999; } -li p:last-child {margin-bottom: 12em !important; margin-top: 6em; font-size: 14px; line-height: 1.5; font-weight: 800 !important; } html {scroll-behavior: smooth; } +li p:last-child {font-size: 14px; font-weight: 800 !important; line-height: 1.5; margin-bottom: 12em !important; margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css index 6afd4a9..c6d1b0d 100644 --- a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important - } - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css index 6afd4a9..c6d1b0d 100644 --- a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important - } - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css index 905e248..5c89a40 100644 --- a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; - } - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css index 905e248..5c89a40 100644 --- a/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_2_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; - } - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css index 5c14ff5..b18f4e0 100644 --- a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important -} - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css index 5c14ff5..b18f4e0 100644 --- a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important -} - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css index 2dc8aba..b0dc652 100644 --- a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; -} - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css index 2dc8aba..b0dc652 100644 --- a/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_2_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; -} - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css index ffbd0e7..8718aed 100644 --- a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important - } - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css index ffbd0e7..8718aed 100644 --- a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important - } - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css index 67a5fe0..49a3b1a 100644 --- a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; - } - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css index 67a5fe0..49a3b1a 100644 --- a/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_4_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; - } - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css index 76b9976..9adc67a 100644 --- a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important -} - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css index 76b9976..9adc67a 100644 --- a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important -} - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css index 19c3f36..8a67610 100644 --- a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; -} - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css index 19c3f36..8a67610 100644 --- a/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_4_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; -} - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css index fa2263f..306b110 100644 --- a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important - } - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css index fa2263f..306b110 100644 --- a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important - } - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css index 60d3152..19529d1 100644 --- a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; - } - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css index 60d3152..19529d1 100644 --- a/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_tab_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; + } + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; - } - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css index ba6d847..1efa5e5 100644 --- a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important -} - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css index ba6d847..1efa5e5 100644 --- a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_not_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999 } +html { + scroll-behavior: smooth +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; + font-weight: 800 !important; line-height: 1.5; - font-weight: 800 !important -} - -html { - scroll-behavior: smooth + margin-bottom: 12em !important; + margin-top: 6em } diff --git a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css index d5a9c42..fb0383d 100644 --- a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; -} - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } diff --git a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css index d5a9c42..fb0383d 100644 --- a/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css +++ b/tests/test_dict2css_/test_dumps_tab_not_indent_closing_brace_trailing_semicolon_sort_keys_not_check_circular_.css @@ -3,14 +3,14 @@ z-index: 999; } +html { + scroll-behavior: smooth; +} + li p:last-child { - margin-bottom: 12em !important; - margin-top: 6em; font-size: 14px; - line-height: 1.5; font-weight: 800 !important; -} - -html { - scroll-behavior: smooth; + line-height: 1.5; + margin-bottom: 12em !important; + margin-top: 6em; } From c463b1c404007fddceb670ee9f0a5f40795dac8d Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Thu, 7 May 2026 23:37:55 +0100 Subject: [PATCH 07/13] Lint --- dict2css/__init__.py | 2 +- tests/test_dict2css.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/dict2css/__init__.py b/dict2css/__init__.py index 374deb4..9ad4c96 100644 --- a/dict2css/__init__.py +++ b/dict2css/__init__.py @@ -69,7 +69,7 @@ """ # Property = Union[Tuple[Union[str, int, None], str], str, int, None] -Property = Union[Sequence, str, int, None] +Property = Union[Sequence, str, int, float, None] Style = Mapping[str, Property] """ diff --git a/tests/test_dict2css.py b/tests/test_dict2css.py index 223a30d..63623e2 100644 --- a/tests/test_dict2css.py +++ b/tests/test_dict2css.py @@ -1,6 +1,6 @@ # stdlib from ipaddress import IPv4Address -from typing import Dict, Mapping, MutableMapping +from typing import Dict, Mapping, MutableMapping, no_type_check # 3rd party import pytest @@ -23,6 +23,7 @@ def boolean_option(name: str, id: str): # noqa: A002,MAN002 # pylint: disable= ) +@no_type_check def test_dumps_not_mapping(): with pytest.raises(TypeError, match="Cannot convert .* to CSS"): dumps([1, 2, 3]) @@ -46,6 +47,7 @@ def test_dumps_not_mapping(): dumps(None) +@no_type_check @boolean_option("check_circular", "check_circular") def test_dumps_unknown_type(check_circular: bool): with pytest.raises(ValueError, match="Object of type .* cannot be represented in CSS"): @@ -55,6 +57,7 @@ def test_dumps_unknown_type(check_circular: bool): dumps({"the_key": IPv4Address("127.0.0.1")}, check_circular=check_circular) +@no_type_check def test_dumps_bad_floats(): with pytest.raises(ValueError, match="Out of range float values are not allowed:"): dumps({"the_key": float("inf")}) @@ -238,6 +241,7 @@ def test_loads_bad_syntax(): loads('\n'.join(style)) +@no_type_check def test_circular_references(): css = { 'a': {'b': 'c'}, @@ -250,7 +254,8 @@ def test_circular_references(): def test_edge_cases(): with pytest.raises(TypeError, match="keys must be strings, not"): - dumps({1234: {'a': 'b'}}) + dumps({1234: {'a': 'b'}}) # type: ignore[dict-item] + with pytest.raises(ValueError, match="Property cannot be empty"): dumps({'a': {'b': ()}}) From d005df04bbcda8c62bacf5e2d32065ae76256d57 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Fri, 8 May 2026 07:50:40 +0100 Subject: [PATCH 08/13] Drop support for Python 3.7 --- tox.ini | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tox.ini b/tox.ini index bf50f19..01d2033 100644 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,7 @@ # You may add new sections, but any changes made to the following sections will be lost: # * tox # * envlists +# * testenv # * testenv:.package # * testenv:py313-dev # * testenv:py313 @@ -35,6 +36,17 @@ test = py38, py39, py310, py311, py312, py313, pypy38, pypy39 qa = mypy, lint cov = py39, coverage +[testenv] +setenv = + PYTHONDEVMODE=1 + PIP_DISABLE_PIP_VERSION_CHECK=1 + SETUPTOOLS_USE_DISTUTILS=stdlib +download = True +deps = -r{toxinidir}/tests/requirements.txt +commands = + python --version + python -m pytest --cov=dict2css -r aR tests/ {posargs} + [testenv:.package] setenv = PYTHONDEVMODE=1 From 57956a396255b4ffad84f11bb2c691b8e5144085 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Fri, 8 May 2026 07:59:36 +0100 Subject: [PATCH 09/13] Correctly handle None --- dict2css/serializer.py | 4 +++- tests/test_dict2css.py | 5 +---- tests/test_dict2css_/test_dump_minify.css | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dict2css/serializer.py b/dict2css/serializer.py index bedef56..8c3cec5 100644 --- a/dict2css/serializer.py +++ b/dict2css/serializer.py @@ -42,7 +42,7 @@ __all__ = ["CSSSerializer"] #: Python objects that can be serialized to CSS -Serializable = Union[dict, list, tuple, int, float, str, bool] +Serializable = Union[dict, list, tuple, int, float, str, bool, None] #: A callable that takes in an object that is not serializable and returns a serializable object DefaultInterface = Union[ @@ -220,6 +220,8 @@ def _iterencode(self, obj: Any, indent_level: int) -> Iterable[str]: yield "true" elif obj is False: yield "false" + elif obj is None: + yield "none" elif isinstance(obj, int): yield repr(obj) elif isinstance(obj, float): diff --git a/tests/test_dict2css.py b/tests/test_dict2css.py index 63623e2..ad275bb 100644 --- a/tests/test_dict2css.py +++ b/tests/test_dict2css.py @@ -50,9 +50,6 @@ def test_dumps_not_mapping(): @no_type_check @boolean_option("check_circular", "check_circular") def test_dumps_unknown_type(check_circular: bool): - with pytest.raises(ValueError, match="Object of type .* cannot be represented in CSS"): - dumps({"the_key": None}, check_circular=check_circular) - with pytest.raises(ValueError, match="Object of type .* cannot be represented in CSS"): dumps({"the_key": IPv4Address("127.0.0.1")}, check_circular=check_circular) @@ -143,7 +140,7 @@ def test_dumps( def test_dump_minify(advanced_file_regression: AdvancedFileRegressionFixture, tmp_pathplus: PathPlus): stylesheet: Dict[str, Style] = { - ".wy-nav-content": {"max-width": (rem(1200), IMPORTANT)}, + ".wy-nav-content": {"max-width": (rem(1200), IMPORTANT), "box-shadow": None}, "li p:last-child": { "margin-bottom": (em(12), IMPORTANT), "margin-top": em(6), diff --git a/tests/test_dict2css_/test_dump_minify.css b/tests/test_dict2css_/test_dump_minify.css index 210a232..d65e856 100644 --- a/tests/test_dict2css_/test_dump_minify.css +++ b/tests/test_dict2css_/test_dump_minify.css @@ -1 +1 @@ -.wy-nav-content{max-width:1200rem !important}li p:last-child{margin-bottom:12em !important;margin-top:6em}html{scroll-behavior:smooth} +.wy-nav-content{max-width:1200rem !important;box-shadow:none}li p:last-child{margin-bottom:12em !important;margin-top:6em}html{scroll-behavior:smooth} From 483da8fbff657e711e8ff30228b9369ad8e1523e Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Fri, 8 May 2026 08:05:53 +0100 Subject: [PATCH 10/13] Update docs --- dict2css/__init__.py | 10 +++++++--- dict2css/serializer.py | 10 ++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/dict2css/__init__.py b/dict2css/__init__.py index 9ad4c96..13c3918 100644 --- a/dict2css/__init__.py +++ b/dict2css/__init__.py @@ -3,9 +3,6 @@ # __init__.py """ A μ-library for constructing cascasing style sheets from Python dictionaries. - -.. latex:vspace:: 10px -.. seealso:: `css-parser `_, which this library builds upon. """ # # Copyright © 2020-2021 Dominic Davis-Foster @@ -143,6 +140,7 @@ def dumps( :return: The style sheet as a string. .. versionchanged:: 0.2.0 Added support for media at-rules. + .. versionchanged:: 0.5.0 New implementation. Output may differ slightly from previous css-parser based one. """ serializer = CSSSerializer( @@ -224,6 +222,8 @@ def dump( * ``fp`` now accepts :py:obj:`domdf_python_tools.typing.PathLike` objects, representing the path of a file to write to. * Added support for media at-rules. + + .. versionchanged:: 0.5.0 New implementation. Output may differ slightly from previous css-parser based one. """ css = dumps( @@ -247,10 +247,13 @@ def loads(styles: str) -> MutableMapping[str, MutableMapping[str, Any]]: Parse a style sheet and return its dictionary representation. .. versionadded:: 0.2.0 + .. versionchanged:: 0.5.0 New implementation. Output may differ slightly from previous css-parser based one. :param styles: :return: The style sheet as a dictionary. + + .. latex:clearpage:: """ stylesheet = tinycss2.parse_blocks_contents(styles, skip_comments=True, skip_whitespace=True) @@ -298,6 +301,7 @@ def load(fp: Union[PathLike, IO]) -> MutableMapping[str, MutableMapping[str, Any Parse a cascading style sheet from the given file and return its dictionary representation. .. versionadded:: 0.2.0 + .. versionchanged:: 0.5.0 New implementation. Output may differ slightly from previous css-parser based one. :param fp: An open file handle, or the filename of a file to write to. diff --git a/dict2css/serializer.py b/dict2css/serializer.py index 8c3cec5..6169b32 100644 --- a/dict2css/serializer.py +++ b/dict2css/serializer.py @@ -72,7 +72,7 @@ class CSSSerializer: r""" - Serializes a :class:`~.StyleSheet` and its parts. + Serializes a dictionary to CSS. This controls the formatting of the style sheet. @@ -83,7 +83,9 @@ class CSSSerializer: :param sort_keys: Sort dictionary keys alphabetically. :param check_circular: Check for circular references. - .. autosummary-widths:: 5/16 + .. versionchanged:: 0.5.0 New implementation. Output may differ slightly from previous css-parser based one. + + .. autosummary-widths:: 1/4 """ def __init__( @@ -160,9 +162,9 @@ def encode(self, obj: Mapping) -> str: """ Return a CSS representation of a Python dictionary. - :param obj: The Python object to be serialized + :param obj: The Python dictionary to be serialized - :returns: The CSS string representation of the Python object + :returns: The CSS string representation of the Python dictionary """ return ''.join(self.iterencode(obj)) From 4443327e9836ac4a2c06e7320625b6aa15120155 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Fri, 8 May 2026 10:43:23 +0100 Subject: [PATCH 11/13] Fix tox.ini config --- tox.ini | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tox.ini b/tox.ini index 01d2033..74f65f8 100644 --- a/tox.ini +++ b/tox.ini @@ -215,12 +215,3 @@ package = dict2css [pytest] addopts = --color yes --durations 25 timeout = 300 - -[testenv] -setenv = - PYTHONDEVMODE=1 - PIP_DISABLE_PIP_VERSION_CHECK=1 -deps = -r{toxinidir}/tests/requirements.txt -commands = - python --version - python -m pytest --cov=dict2css -r aR tests/ {posargs} From f0cdf0cb38ac5bd2b7608a3ccde66d606ad8305a Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Fri, 8 May 2026 11:30:27 +0100 Subject: [PATCH 12/13] Update changelog --- doc-source/changelog.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc-source/changelog.rst b/doc-source/changelog.rst index 7cc0218..ed0c691 100644 --- a/doc-source/changelog.rst +++ b/doc-source/changelog.rst @@ -6,7 +6,10 @@ Changelog 0.5.0 ------- -Drop support for Python 3.7 +* Drop support for Python 3.7 +* Reimplement ``load(s)`` using ``tinycss2`` and ``dump(s)`` using a modified version of ``ujson5`` + +.. changelog:: 0.5.0 0.4.0 From 46052d9fd4d67983dea1a4e3e2b8e98a8149b246 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Fri, 8 May 2026 20:31:22 +0100 Subject: [PATCH 13/13] Update changelog --- doc-source/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc-source/changelog.rst b/doc-source/changelog.rst index ed0c691..4c38a44 100644 --- a/doc-source/changelog.rst +++ b/doc-source/changelog.rst @@ -8,6 +8,7 @@ Changelog * Drop support for Python 3.7 * Reimplement ``load(s)`` using ``tinycss2`` and ``dump(s)`` using a modified version of ``ujson5`` +* Remove ``StyleSheet`` and ``make_style`` .. changelog:: 0.5.0