From fea9f2e7ca777935066e4a09545c6f2267e26c1f Mon Sep 17 00:00:00 2001 From: Martin Owens Date: Mon, 20 Apr 2015 14:47:12 -0400 Subject: [PATCH] Improve CSS query with getter and setter, keep order of css entries and add tests --- pyquery/pyquery.py | 69 +++++++++++++++++++++---------------------- tests/test_css.html | 10 +++++++ tests/test_pyquery.py | 37 +++++++++++++++++++++++ 3 files changed, 80 insertions(+), 36 deletions(-) create mode 100644 tests/test_css.html diff --git a/pyquery/pyquery.py b/pyquery/pyquery.py index 628a32d..4f9890f 100644 --- a/pyquery/pyquery.py +++ b/pyquery/pyquery.py @@ -12,6 +12,7 @@ import types import sys +from collections import OrderedDict PY3k = sys.version_info >= (3,) @@ -883,44 +884,40 @@ def toggle_class(self, value): tag.set('class', ' '.join(classes)) return self - def css(self, *args, **kwargs): - """css attributes manipulation - """ - - attr = value = no_default - length = len(args) - if length == 1: - attr = args[0] - elif length == 2: - attr, value = args - elif kwargs: - attr = kwargs + def css(self, propertyName, value=no_default): + """css attributes manipulation""" + def clean_css(styles): + return OrderedDict((key.strip().replace('_', '-'), value.strip()) + for key, value in styles.items()) + + if isinstance(propertyName, basestring): + getter = value is no_default + attrs = {propertyName: not getter and value or ''} + elif isinstance(propertyName, dict): + attrs = propertyName + getter = False + elif isinstance(propertyName, list): + attrs = OrderedDict((key, '') for key in propertyName) + getter = True else: - raise ValueError('Invalid arguments %s %s' % (args, kwargs)) + raise ValueError('Invalid arguments %s' % str(propertyName)) + attrs = clean_css(attrs) - if isinstance(attr, dict): - for tag in self: - stripped_keys = [key.strip().replace('_', '-') - for key in attr.keys()] - current = [el.strip() - for el in (tag.get('style') or '').split(';') - if el.strip() - and not el.split(':')[0].strip() in stripped_keys] - for key, value in attr.items(): - key = key.replace('_', '-') - current.append('%s: %s' % (key, value)) - tag.set('style', '; '.join(current)) - elif isinstance(value, basestring): - attr = attr.replace('_', '-') - for tag in self: - current = [ - el.strip() - for el in (tag.get('style') or '').split(';') - if (el.strip() and - not el.split(':')[0].strip() == attr.strip())] - current.append('%s: %s' % (attr, value)) - tag.set('style', '; '.join(current)) - return self + for tag in self: + style = (tag.get('style') or '').split(';') + styles = OrderedDict(e.split(':', 1) for e in style if ':' in e) + styles = clean_css(styles) + + if getter: + # The getter always returns for the first element in jquery + ret = [ styles.get(key, no_default) for key in attrs.keys() ] + return isinstance(propertyName, list) and ret or ''.join(ret[:1]) + + styles.update(attrs) + styles = [ ': '.join(e) for e in styles.items() if all(e) ] + tag.set('style', '; '.join(styles) + ';') + # Return's self on write (but not on read) + return self css = FlexibleElement(pget=css, pset=css) diff --git a/tests/test_css.html b/tests/test_css.html new file mode 100644 index 0000000..d2f47f6 --- /dev/null +++ b/tests/test_css.html @@ -0,0 +1,10 @@ + + +

Hello world !

+ +

+hello python ! +

+
CSS Too!
+ + diff --git a/tests/test_pyquery.py b/tests/test_pyquery.py index 37662c1..52ca276 100644 --- a/tests/test_pyquery.py +++ b/tests/test_pyquery.py @@ -34,6 +34,7 @@ def not_py3k(func): dirname = os.path.dirname(os.path.abspath(__file__)) docs = os.path.join(os.path.dirname(dirname), 'docs') path_to_html_file = os.path.join(dirname, 'test.html') +path_to_css_file = os.path.join(dirname, 'test_css.html') path_to_invalid_file = os.path.join(dirname, 'invalid.xml') @@ -265,6 +266,42 @@ def test_closest(self): assert self.klass('.node3', self.html).closest('form') == [] +class TestCss(TestCase): + def setUp(self): + self.doc = pq(filename=path_to_css_file) + self.tags = self.doc('#css') + + def test_read_css(self): + self.assertEqual(self.tags.css('color'), 'red') + + def test_read_list(self): + self.assertEqual(self.tags.css(['color']), ['red']) + self.assertEqual(self.tags.css(['color', 'border']), + ['red', '2px solid black']) + + def test_write_css_new(self): + self.tags.css('font-size', '4px') + self.assertEqual(self.tags[0].get('style'), + 'color: red; border: 2px solid black; font-size: 4px;') + + def test_remove_css(self): + self.tags.css('color', None) + self.assertEqual(self.tags[0].get('style'), + 'border: 2px solid black;') + + def test_write_css_old(self): + # Note, our jquery modifies the css in place rather than reordering. + self.tags.css('color', 'blue') + self.assertEqual(self.tags.css('color'), 'blue') + self.assertEqual(self.tags[0].get('style'), + 'color: blue; border: 2px solid black;') + + def test_write_dict(self): + self.tags.css({'color': 'green', 'font-size': '16px'}) + self.assertEqual(self.tags[0].get('style'), + 'color: green; border: 2px solid black; font-size: 16px;') + + class TestOpener(TestCase): def test_open_filename(self):