Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
jaraco committed Apr 5, 2021
2 parents 2c083d4 + 46b0e7b commit faa8acd
Show file tree
Hide file tree
Showing 40 changed files with 409 additions and 668 deletions.
97 changes: 54 additions & 43 deletions CHANGES.rst

Large diffs are not rendered by default.

19 changes: 0 additions & 19 deletions LICENSE

This file was deleted.

47 changes: 9 additions & 38 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,47 +61,20 @@ There is also a low-traffic `cssutils discussion group <http://groups.google.com

Compatibility
=============
cssutils is developed on standard Python but works under Python 2.x (from 2.5, 2.7.6 tested), 3.x (v3.3.3 tested) and Jython (from 2.5.1). IronPython has not been tested yet but might work? Python 2.4 and older are not supported since cssutils 0.9.8 anymore.
cssutils is not thread safe, please beware!

License
=======
Copyright 2005 - 2013 Christof Hoeke

cssutils is published under the LGPL 3 or later

cssutils is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

cssutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with cssutils. If not, see http://www.gnu.org/licenses.


Installation
============
From 0.9.6 cssutils uses `Distribute <http://pypi.python.org/pypi/distribute>`_

After installing Distribute use::
cssutils is developed on modern Python versions. Check the package metadata
for compatibilty.

> easy_install cssutils

to install the latest version of cssutils.

Alternatively download the provided source distribution. Expand the file and from a command line install with::

> python setup.py install

To uninstall remove any registrations of cssutils eggs with Distribute and remove the eggs which should be installed at PYTHONDIR/Lib/site-packages/cssutils too.
Beware, cssutils is known to be thread unsafe.


Example
=======
::

# -*- coding: utf-8 -*-
import cssutils

css = u'''/* a comment with umlaut &auml; */
css = '''/* a comment with umlaut &auml; */
@namespace html "http://www.w3.org/1999/xhtml";
@variables { BG: #fff }
html|a { color:red; background: var(BG) }'''
Expand Down Expand Up @@ -144,17 +117,15 @@ results in::
}


Documentation
=============
The current documenation can be found at http://packages.python.org/cssutils/


Kind Request
============
cssutils is far from being perfect or even complete. If you find any bugs (especially specification violations) or have problems or suggestions please put them in the `Issue Tracker <https://bitbucket.org/cthedot/cssutils/issues>`_ at Bitbucket.

cssutils is far from being perfect or even complete. If you find any bugs (especially specification violations) or have problems or suggestions please put them in the `Issue Tracker <https://github.com/jaraco/cssutils/issues>`_.


Thanks
======
Thanks to Simon Sapin, Jason R. Coombs and Walter Doerwald for patches, help and discussion. Thanks to Kevin D. Smith for the value validating module. Thanks also to Cory Dodt, Tim Gerla, James Dobson and Amit Moscovich for helpful suggestions and code patches. Thanks to Fredrik Hedman for help on port of encutils to Python 3.

Special thanks to Christof Höke for seminal creation of the library.

Thanks to Simon Sapin, Jason R. Coombs, and Walter Doerwald for patches, help and discussion. Thanks to Kevin D. Smith for the value validating module. Thanks also to Cory Dodt, Tim Gerla, James Dobson and Amit Moscovich for helpful suggestions and code patches. Thanks to Fredrik Hedman for help on port of encutils to Python 3.
168 changes: 80 additions & 88 deletions cssutils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,5 @@
#!/usr/bin/env python
"""cssutils - CSS Cascading Style Sheets library for Python
Copyright (C) 2004-2013 Christof Hoeke
cssutils is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
CSS Cascading Style Sheets library for Python
A Python package to parse and build CSS Cascading Style Sheets. DOM only, not
any rendering facilities!
Expand Down Expand Up @@ -69,19 +53,11 @@
stylesheets "in the wild" while at the same time implement the official APIs
which are well documented. Some minor extensions are provided as well.
Please visit http://cthedot.de/cssutils/ for more details.
Tested with Python 2.7.6 and 3.3.3 on Windows 8.1 64bit.
Please visit https://cssutils.readthedocs.io/ for more details.
This library may be used ``from cssutils import *`` which
import subpackages ``css`` and ``stylesheets``, CSSParser and
CSSSerializer classes only.
Example::
Usage may be::
>>> from cssutils import *
>>> from cssutils import CSSParser
>>> parser = CSSParser()
>>> sheet = parser.parseString('a { color: red}')
Expand All @@ -94,17 +70,12 @@
"""
__all__ = ['css', 'stylesheets', 'CSSParser', 'CSSSerializer']

import sys

if sys.version_info < (2, 6):
bytes = str

import os.path
import urllib.request
import urllib.parse
import urllib.error
import urllib.parse
import xml.dom
import itertools
import functools

from . import errorhandler
from . import css
Expand Down Expand Up @@ -160,23 +131,26 @@ def createCSSStyleSheet(self, title, media):

warning = (
"Deprecated, see "
"https://web.archive.org/web/20200701035537/"
"https://bitbucket.org/cthedot/cssutils/issues/69#comment-30669799"
)
warnings.warn(warning, DeprecationWarning)
return css.CSSStyleSheet(title=title, media=media)

def createDocument(self, *args, **kwargs):
# sometimes cssutils is picked automatically for
# xml.dom.getDOMImplementation, so we should provide an implementation
# see https://bitbucket.org/cthedot/cssutils/issues/69
# xml.dom.getDOMImplementation, so provide an implementation
# see (https://web.archive.org/web/20200701035537/
# https://bitbucket.org/cthedot/cssutils/issues/69)
import xml.dom.minidom as minidom

return minidom.DOMImplementation().createDocument(*args, **kwargs)

def createDocumentType(self, *args, **kwargs):
# sometimes cssutils is picked automatically for
# xml.dom.getDOMImplementation, so we should provide an implementation
# see https://bitbucket.org/cthedot/cssutils/issues/69
# xml.dom.getDOMImplementation, so provide an implementation
# see (https://web.archive.org/web/20200701035537/
# https://bitbucket.org/cthedot/cssutils/issues/69)
import xml.dom.minidom as minidom

return minidom.DOMImplementation().createDocumentType(*args, **kwargs)
Expand Down Expand Up @@ -219,11 +193,18 @@ def parseStyle(*a, **k):
# set "ser", default serializer
def setSerializer(serializer):
"""Set the global serializer used by all class in cssutils."""
global ser
ser = serializer
globals().update(ser=serializer)


def _style_declarations(base):
"recursive generator to find all CSSStyleDeclarations"
for rule in getattr(base, 'cssRules', ()):
yield from _style_declarations(rule)
if hasattr(base, 'style'):
yield base.style

def getUrls(sheet): # noqa: C901

def getUrls(sheet):
"""Retrieve all ``url(urlstring)`` values (in e.g.
:class:`cssutils.css.CSSImportRule` or :class:`cssutils.css.CSSValue`
objects of given `sheet`.
Expand All @@ -234,60 +215,71 @@ def getUrls(sheet): # noqa: C901
This function is a generator. The generated URL values exclude ``url(`` and
``)`` and surrounding single or double quotes.
"""
for importrule in (r for r in sheet if r.type == r.IMPORT_RULE):
yield importrule.href

def styleDeclarations(base):
"recursive generator to find all CSSStyleDeclarations"
if hasattr(base, 'cssRules'):
for rule in base.cssRules:
for s in styleDeclarations(rule):
yield s
elif hasattr(base, 'style'):
yield base.style

for style in styleDeclarations(sheet):
for p in style.getProperties(all=True):
for v in p.propertyValue:
if v.type == 'URI':
yield v.uri


def replaceUrls(sheetOrStyle, replacer, ignoreImportRules=False): # noqa: C901
imports = (rule.href for rule in sheet if rule.type == rule.IMPORT_RULE)

other = (
value.uri
for style in _style_declarations(sheet)
for value in _uri_values(style)
)

return itertools.chain(imports, other)


def _uri_values(style):
return (
value
for prop in style.getProperties(all=True)
for value in prop.propertyValue
if value.type == 'URI'
)


_flatten = itertools.chain.from_iterable


@functools.singledispatch
def replaceUrls(sheet, replacer, ignoreImportRules=False):
"""Replace all URLs in :class:`cssutils.css.CSSImportRule` or
:class:`cssutils.css.CSSValue` objects of given `sheetOrStyle`.
:class:`cssutils.css.CSSValue` objects of given `sheet`.
:param sheetOrStyle:
a :class:`cssutils.css.CSSStyleSheet` or a
:class:`cssutils.css.CSSStyleDeclaration` which is changed in place
:param sheet:
a :class:`cssutils.css.CSSStyleSheet` to be modified in place.
:param replacer:
a function which is called with a single argument `url` which
is the current value of each url() excluding ``url(``, ``)`` and
surrounding (single or double) quotes.
:param ignoreImportRules:
if ``True`` does not call `replacer` with URLs from @import rules.
"""
if not ignoreImportRules and not isinstance(sheetOrStyle, css.CSSStyleDeclaration):
for importrule in (r for r in sheetOrStyle if r.type == r.IMPORT_RULE):
importrule.href = replacer(importrule.href)

def styleDeclarations(base):
"recursive generator to find all CSSStyleDeclarations"
if hasattr(base, 'cssRules'):
for rule in base.cssRules:
for s in styleDeclarations(rule):
yield s
elif hasattr(base, 'style'):
yield base.style
elif isinstance(sheetOrStyle, css.CSSStyleDeclaration):
# base is a style already
yield base

for style in styleDeclarations(sheetOrStyle):
for p in style.getProperties(all=True):
for v in p.propertyValue:
if v.type == v.URI:
v.uri = replacer(v.uri)
imports = (
rule
for rule in sheet
if rule.type == rule.IMPORT_RULE and not ignoreImportRules
)
for rule in imports:
rule.href = replacer(rule.href)

for value in _flatten(map(_uri_values, _style_declarations(sheet))):
value.uri = replacer(value.uri)


@replaceUrls.register(css.CSSStyleDeclaration)
def _(style, replacer, ignoreImportRules=False):
"""Replace all URLs in :class:`cssutils.css.CSSImportRule` or
:class:`cssutils.css.CSSValue` objects of given `style`.
:param style:
a :class:`cssutils.css.CSSStyleDeclaration` to be modified in place.
:param replacer:
a function which is called with a single argument `url` which
is the current value of each url() excluding ``url(``, ``)`` and
surrounding (single or double) quotes.
:param ignoreImportRules:
not applicable, ignored.
"""
for value in _uri_values(style):
value.uri = replacer(value.uri)


def resolveImports(sheet, target=None): # noqa: C901
Expand Down
16 changes: 10 additions & 6 deletions cssutils/_fetch.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
"""Default URL reading functions"""
__all__ = ['_defaultFetcher']

import encutils
from . import errorhandler
import urllib.request
import urllib.error
import urllib.parse

try:
from importlib import metadata
except ImportError:
import importlib_metadata as metadata

import encutils
from . import errorhandler

log = errorhandler.ErrorHandler()

Expand All @@ -18,9 +23,8 @@ def _defaultFetcher(url):
"""
try:
request = urllib.request.Request(url)
# TODO: load version from metadata
VERSION = '???'
agent = 'cssutils %s (http://www.cthedot.de/cssutils/)' % VERSION
version = metadata.version('cssutils')
agent = f'cssutils {version} (https://pypi.org/project/cssutils)'
request.add_header('User-agent', agent)
res = urllib.request.urlopen(request)
except urllib.error.HTTPError as e:
Expand Down

0 comments on commit faa8acd

Please sign in to comment.