PK3iRdict2css/helpers.py#!/usr/bin/env python3 # # helpers.py """ Helper functions. .. versionadded:: 0.2.0 """ # # Copyright © 2020-2021 Dominic Davis-Foster # # 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 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib from typing import Union __all__ = ["em", "px", "rem"] def px(val: Union[int, float, str]) -> str: """ Helper function to format a number as a value in pixels. :param val: """ return f"{val}px" def em(val: Union[int, float, str]) -> str: """ Helper function to format a number as a value in em. :param val: """ return f"{val}em" def rem(val: Union[int, float, str]) -> str: """ Helper function to format a number as a value in rem. :param val: """ return f"{val}rem" PK3iRZ![ dict2css/serializer.py#!/usr/bin/env python3 # # serializer.py """ Serializer for cascading style sheets. .. versionadded:: 0.2.0 """ # # Copyright © 2021 Dominic Davis-Foster # # 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 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib from contextlib import contextmanager from typing import Iterator # 3rd party import css_parser # type: ignore from domdf_python_tools.words import TAB __all__ = ["CSSSerializer"] class CSSSerializer(css_parser.CSSSerializer): r""" Serializes a :class:`~.StyleSheet` and its parts. This controls the formatting of the style sheet. :param indent: The indent to use, such as a tab (``\t``), two spaces or four spaces. :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. """ def __init__( self, *, indent: str = TAB, trailing_semicolon: bool = False, 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 def reset_style(self) -> None: """ Reset the serializer to its default style. """ # Reset CSS Parser to defaults self.prefs.useDefaults() if self.minify: self.prefs.useMinified() else: # Formatting preferences self.prefs.omitLastSemicolon = not self.trailing_semicolon self.prefs.indentClosingBrace = self.indent_closing_brace self.prefs.indent = self.indent @contextmanager def use(self) -> Iterator: """ Contextmanager to use this serializer for the scope of the ``with`` block. """ # 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 PK3iRdict2css/py.typedPK3iRiIQ*'*'dict2css/__init__.py#!/usr/bin/env python3 # # __init__.py """ A μ-library for constructing cascasing style sheets from Python dictionaries. .. seealso:: `css-parser `_, which this library builds upon. """ # # Copyright © 2020-2021 Dominic Davis-Foster # # 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 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib from io import TextIOBase from typing import IO, Any, Dict, Mapping, MutableMapping, Sequence, Union, cast # 3rd party import css_parser # type: ignore from domdf_python_tools.paths import PathPlus from domdf_python_tools.typing import PathLike from domdf_python_tools.words import TAB # this package from dict2css.helpers import em, px, rem from dict2css.serializer import CSSSerializer __author__: str = "Dominic Davis-Foster" __copyright__: str = "2020-2021 Dominic Davis-Foster" __license__: str = "MIT License" __version__: str = "0.2.2" __email__: str = "dominic@davis-foster.co.uk" __all__ = [ "dumps", "dump", "load", "loads", "StyleSheet", "make_style", "Style", "IMPORTANT", ] #: The string ``'important'``. IMPORTANT = "important" # Property = Union[Tuple[Union[str, int, None], str], str, int, None] Property = Union[Sequence, str, int, None] Style = Mapping[str, Property] """ Type annotation representing a style for :func:`~.make_style` and :func:`~.dumps`. The keys are CSS properties. The values can be either: * A :class:`str`, :class:`float` or :py:obj:`None`, giving the value of the property. * A :class:`tuple` of the property's value (as above) and the priority, such as :data:`~.IMPORTANT` which sets ``!important`` on the property. """ def dumps( styles: Mapping[str, Union[Style, Mapping]], *, indent: str = TAB, trailing_semicolon: bool = False, indent_closing_brace: bool = False, minify: bool = False, ) -> str: r""" Construct a cascading style sheet from a dictionary. ``styles`` is a mapping of CSS selector strings to styles, which map property names to their values: .. code-block:: python styles = {".wy-nav-content": {"max-width": (px(1200), IMPORTANT)}} print(dumps(styles)) .. code-block:: css .wy-nav-content { max-width: 1200px !important } See the :py:obj:`~.Styles` object for more information on the layout. The keys can also be media at-rules, with the values mappings of property names to their values: .. code-block:: python styles = { "@media screen and (min-width: 870px)": { ".wy-nav-content": {"max-width": (px(1200), IMPORTANT)}, }, } .. code-block:: css @media screen and (min-width: 870px) { .wy-nav-content { max-width: 1200px !important } } print(dumps(styles)) :param styles: A mapping of CSS selectors to styles. :param indent: The indent to use, such as a tab (``\t``), two spaces or four spaces. :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. :return: The style sheet as a string. .. versionchanged:: 0.2.2 Added support for media at-rules. """ serializer = CSSSerializer( indent=indent, trailing_semicolon=trailing_semicolon, indent_closing_brace=indent_closing_brace, minify=minify, ) 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 def dump( styles: Mapping[str, Union[Style, Mapping]], fp: Union[PathLike, IO], *, indent: str = TAB, trailing_semicolon: bool = False, indent_closing_brace: bool = False, minify: bool = False, ) -> None: r""" Construct a cascading style sheet from a dictionary and write it to ``fp``. .. code-block:: python styles = {".wy-nav-content": {"max-width": (px(1200), IMPORTANT)}} dump(styles, ...) .. code-block:: css .wy-nav-content { max-width: 1200px !important } See the :py:obj:`~.Styles` object for more information on the layout. The keys can also be media at-rules, with the values mappings of property names to their values: .. code-block:: python styles = { "@media screen and (min-width: 870px)": { ".wy-nav-content": {"max-width": (px(1200), IMPORTANT)}, }, } dump(styles, ...) .. code-block:: css @media screen and (min-width: 870px) { .wy-nav-content { max-width: 1200px !important } } :param styles: A mapping of CSS selectors to styles. :param fp: An open file handle, or the filename of a file to write to. :param indent: The indent to use, such as a tab (``\t``), two spaces or four spaces. :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. .. versionchanged:: 0.2.2 `fp` now accepts :py:obj:`domdf_python_tools.typing.PathLike` objects, representing the path of a file to write to. .. versionchanged:: 0.2.2 Added support for media at-rules. """ css = dumps( styles, indent=indent, trailing_semicolon=trailing_semicolon, indent_closing_brace=indent_closing_brace, minify=minify, ) if isinstance(fp, TextIOBase): fp.write(css) else: PathPlus(fp).write_clean(css) def loads(styles: str) -> MutableMapping[str, MutableMapping[str, Any]]: r""" Parse a cascading style sheet and return its dictionary representation. .. versionadded:: 0.2.2 :param styles: :return: The style sheet as a dictionary. """ parser = css_parser.CSSParser(validate=False) stylesheet: css_parser.css.CSSStyleSheet = parser.parseString(styles) styles_dict: MutableMapping[str, MutableMapping[str, Any]] = {} def parse_style(style: css_parser.css.CSSStyleDeclaration) -> 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) else: style_dict[prop.name] = 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}"] = {} for child in rule.cssRules: styles_dict[f"@media {rule.media.mediaText}"][child.selectorText] = parse_style(child.style) else: raise NotImplementedError(rule) return styles_dict 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. .. versionadded:: 0.2.2 :param fp: An open file handle, or the filename of a file to write to. :return: The style sheet as a dictionary. """ if isinstance(fp, TextIOBase): styles = fp.read() else: styles = PathPlus(fp).read_text() return loads(styles) class StyleSheet(css_parser.css.CSSStyleSheet): """ Represents a CSS style sheet. """ 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.2 :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) PK3iR ʇSSdict2css-0.2.2.dist-info/WHEELWheel-Version: 1.0 Generator: whey (0.0.3) Root-Is-Purelib: true Tag: py3-none-any PK3iRB &dict2css-0.2.2.dist-info/top_level.txtdict2css PK3iR)dict2css-0.2.2.dist-info/entry_points.txtPK3iRY!dict2css-0.2.2.dist-info/METADATAMetadata-Version: 2.1 Name: dict2css Version: 0.2.2 Summary: A μ-library for constructing cascading style sheets from Python dictionaries. Author-email: Dominic Davis-Foster License: MIT Keywords: css Home-page: https://github.com/sphinx-toolbox/dict2css Project-URL: Issue Tracker, https://github.com/sphinx-toolbox/dict2css/issues Project-URL: Source Code, https://github.com/sphinx-toolbox/dict2css Project-URL: Documentation, https://dict2css.readthedocs.io/en/latest Platform: Windows Platform: macOS Platform: Linux Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Typing :: Typed Requires-Python: >=3.6 Requires-Dist: css-parser==1.0.6 Requires-Dist: domdf-python-tools>=2.2.0 Description-Content-Type: text/x-rst ######### dict2css ######### .. start short_desc **A μ-library for constructing cascading style sheets from Python dictionaries.** .. end short_desc .. start shields .. list-table:: :stub-columns: 1 :widths: 10 90 * - Docs - |docs| |docs_check| * - Tests - |actions_linux| |actions_windows| |actions_macos| |coveralls| * - PyPI - |pypi-version| |supported-versions| |supported-implementations| |wheel| * - Anaconda - |conda-version| |conda-platform| * - Activity - |commits-latest| |commits-since| |maintained| |pypi-downloads| * - QA - |codefactor| |actions_flake8| |actions_mypy| |pre_commit_ci| * - Other - |license| |language| |requires| .. |docs| image:: https://img.shields.io/readthedocs/dict2css/latest?logo=read-the-docs :target: https://dict2css.readthedocs.io/en/latest :alt: Documentation Build Status .. |docs_check| image:: https://github.com/sphinx-toolbox/dict2css/workflows/Docs%20Check/badge.svg :target: https://github.com/sphinx-toolbox/dict2css/actions?query=workflow%3A%22Docs+Check%22 :alt: Docs Check Status .. |actions_linux| image:: https://github.com/sphinx-toolbox/dict2css/workflows/Linux/badge.svg :target: https://github.com/sphinx-toolbox/dict2css/actions?query=workflow%3A%22Linux%22 :alt: Linux Test Status .. |actions_windows| image:: https://github.com/sphinx-toolbox/dict2css/workflows/Windows/badge.svg :target: https://github.com/sphinx-toolbox/dict2css/actions?query=workflow%3A%22Windows%22 :alt: Windows Test Status .. |actions_macos| image:: https://github.com/sphinx-toolbox/dict2css/workflows/macOS/badge.svg :target: https://github.com/sphinx-toolbox/dict2css/actions?query=workflow%3A%22macOS%22 :alt: macOS Test Status .. |actions_flake8| image:: https://github.com/sphinx-toolbox/dict2css/workflows/Flake8/badge.svg :target: https://github.com/sphinx-toolbox/dict2css/actions?query=workflow%3A%22Flake8%22 :alt: Flake8 Status .. |actions_mypy| image:: https://github.com/sphinx-toolbox/dict2css/workflows/mypy/badge.svg :target: https://github.com/sphinx-toolbox/dict2css/actions?query=workflow%3A%22mypy%22 :alt: mypy status .. |requires| image:: https://requires.io/github/sphinx-toolbox/dict2css/requirements.svg?branch=master :target: https://requires.io/github/sphinx-toolbox/dict2css/requirements/?branch=master :alt: Requirements Status .. |coveralls| image:: https://img.shields.io/coveralls/github/sphinx-toolbox/dict2css/master?logo=coveralls :target: https://coveralls.io/github/sphinx-toolbox/dict2css?branch=master :alt: Coverage .. |codefactor| image:: https://img.shields.io/codefactor/grade/github/sphinx-toolbox/dict2css?logo=codefactor :target: https://www.codefactor.io/repository/github/sphinx-toolbox/dict2css :alt: CodeFactor Grade .. |pypi-version| image:: https://img.shields.io/pypi/v/dict2css :target: https://pypi.org/project/dict2css/ :alt: PyPI - Package Version .. |supported-versions| image:: https://img.shields.io/pypi/pyversions/dict2css?logo=python&logoColor=white :target: https://pypi.org/project/dict2css/ :alt: PyPI - Supported Python Versions .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/dict2css :target: https://pypi.org/project/dict2css/ :alt: PyPI - Supported Implementations .. |wheel| image:: https://img.shields.io/pypi/wheel/dict2css :target: https://pypi.org/project/dict2css/ :alt: PyPI - Wheel .. |conda-version| image:: https://img.shields.io/conda/v/domdfcoding/dict2css?logo=anaconda :target: https://anaconda.org/domdfcoding/dict2css :alt: Conda - Package Version .. |conda-platform| image:: https://img.shields.io/conda/pn/domdfcoding/dict2css?label=conda%7Cplatform :target: https://anaconda.org/domdfcoding/dict2css :alt: Conda - Platform .. |license| image:: https://img.shields.io/github/license/sphinx-toolbox/dict2css :target: https://github.com/sphinx-toolbox/dict2css/blob/master/LICENSE :alt: License .. |language| image:: https://img.shields.io/github/languages/top/sphinx-toolbox/dict2css :alt: GitHub top language .. |commits-since| image:: https://img.shields.io/github/commits-since/sphinx-toolbox/dict2css/v0.2.2 :target: https://github.com/sphinx-toolbox/dict2css/pulse :alt: GitHub commits since tagged version .. |commits-latest| image:: https://img.shields.io/github/last-commit/sphinx-toolbox/dict2css :target: https://github.com/sphinx-toolbox/dict2css/commit/master :alt: GitHub last commit .. |maintained| image:: https://img.shields.io/maintenance/yes/2021 :alt: Maintenance .. |pypi-downloads| image:: https://img.shields.io/pypi/dm/dict2css :target: https://pypi.org/project/dict2css/ :alt: PyPI - Downloads .. |pre_commit_ci| image:: https://results.pre-commit.ci/badge/github/sphinx-toolbox/dict2css/master.svg :target: https://results.pre-commit.ci/latest/github/sphinx-toolbox/dict2css/master :alt: pre-commit.ci status .. end shields Installation -------------- .. start installation ``dict2css`` can be installed from PyPI or Anaconda. To install with ``pip``: .. code-block:: bash $ python -m pip install dict2css To install with ``conda``: * First add the required channels .. code-block:: bash $ conda config --add channels http://conda.anaconda.org/conda-forge $ conda config --add channels http://conda.anaconda.org/domdfcoding * Then install .. code-block:: bash $ conda install dict2css .. end installation PK3iRl}(( dict2css-0.2.2.dist-info/LICENSECopyright (c) 2021 Dominic Davis-Foster 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 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK3iR%dict2css-0.2.2.dist-info/RECORDdict2css/helpers.py,sha256=RjMJ5bb9l95FXnicnRRJMY_b_WwMlCc-YIdD1BCNRcY,1747 dict2css/serializer.py,sha256=FoJZF92puVA49U0uQfDxAycH-1Y26kWz4yxJFXuNX1w,2953 dict2css/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 dict2css/__init__.py,sha256=Dr2RCa1ZLEwWU1xygNqqCHEV7ivGCf1SL8lPF6E407A,10026 dict2css-0.2.2.dist-info/WHEEL,sha256=mp1yIFRKHFTzLFJ4MT815UdzBlT3qnfk1upvWSXPXJU,83 dict2css-0.2.2.dist-info/top_level.txt,sha256=74xuxX0aoYYFVC_stjax02texpiblBgHwZrzRz91oXI,9 dict2css-0.2.2.dist-info/entry_points.txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 dict2css-0.2.2.dist-info/METADATA,sha256=-BsRZl1Yi3JcoIpT-6s78kppZDmS7oGOqzacz7OAWwE,6833 dict2css-0.2.2.dist-info/LICENSE,sha256=TXanB-tJYmHhByFsynyh-UGwh2VdTVVjbZeFz1utSWc,1064 PK3iRdict2css/helpers.pyPK3iRZ![ dict2css/serializer.pyPK3iRdict2css/py.typedPK3iRiIQ*'*'dict2css/__init__.pyPK3iR ʇSSL:dict2css-0.2.2.dist-info/WHEELPK3iRB &:dict2css-0.2.2.dist-info/top_level.txtPK3iR)(;dict2css-0.2.2.dist-info/entry_points.txtPK3iRY!o;dict2css-0.2.2.dist-info/METADATAPK3iRl}(( _Vdict2css-0.2.2.dist-info/LICENSEPK3iR%Zdict2css-0.2.2.dist-info/RECORDPK ]