~corey.bryant/ubuntu/wily/python-pyscss/thedac

« back to all changes in this revision

Viewing changes to .pc/python-3.2-six.u.patch/scss/types.py

  • Committer: Package Import Robot
  • Author(s): Thomas Goirand
  • Date: 2014-06-26 12:10:36 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20140626121036-3dv13zn5zptk9fpx
Tags: 1.2.0.post3-1
* Team upload.
* New upstream release (Closes: #738776).
* Added a debian/gbp.conf.
* Added missing ${python:Depends}
* Added Python 3 support.
* Removed duplicate debhelper build-depends.
* Cannonical VCS URLs.
* Standards-Version: is now 3.9.5.
* Added a watch file.
* override dh helpers which the package doesn't use.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from __future__ import absolute_import
 
2
from __future__ import division
 
3
from __future__ import print_function
 
4
 
 
5
import colorsys
 
6
import operator
 
7
 
 
8
import six
 
9
 
 
10
from scss.cssdefs import COLOR_LOOKUP, COLOR_NAMES, ZEROABLE_UNITS, convert_units_to_base_units, cancel_base_units, count_base_units
 
11
from scss.util import escape
 
12
 
 
13
 
 
14
################################################################################
 
15
# pyScss data types:
 
16
 
 
17
class Value(object):
 
18
    is_null = False
 
19
    sass_type_name = u'unknown'
 
20
 
 
21
    def __repr__(self):
 
22
        return '<%s(%s)>' % (self.__class__.__name__, repr(self.value))
 
23
 
 
24
    # Sass values are all true, except for booleans and nulls
 
25
    def __bool__(self):
 
26
        return True
 
27
 
 
28
    def __nonzero__(self):
 
29
        # Py 2's name for __bool__
 
30
        return self.__bool__()
 
31
 
 
32
    # All Sass scalars also act like one-element spaced lists
 
33
    use_comma = False
 
34
 
 
35
    def __iter__(self):
 
36
        return iter((self,))
 
37
 
 
38
    def __len__(self):
 
39
        return 1
 
40
 
 
41
    def __getitem__(self, key):
 
42
        if key not in (-1, 0):
 
43
            raise IndexError(key)
 
44
 
 
45
        return self
 
46
 
 
47
    def __contains__(self, item):
 
48
        return self == item
 
49
 
 
50
    ### NOTE: From here on down, the operators are exposed to Sass code and
 
51
    ### thus should ONLY return Sass types
 
52
 
 
53
    # Reasonable default for equality
 
54
    def __eq__(self, other):
 
55
        return Boolean(
 
56
            type(self) == type(other) and self.value == other.value)
 
57
 
 
58
    def __ne__(self, other):
 
59
        return Boolean(not self.__eq__(other))
 
60
 
 
61
    # Only numbers support ordering
 
62
    def __lt__(self, other):
 
63
        raise TypeError("Can't compare %r with %r" % (self, other))
 
64
 
 
65
    def __le__(self, other):
 
66
        raise TypeError("Can't compare %r with %r" % (self, other))
 
67
 
 
68
    def __gt__(self, other):
 
69
        raise TypeError("Can't compare %r with %r" % (self, other))
 
70
 
 
71
    def __ge__(self, other):
 
72
        raise TypeError("Can't compare %r with %r" % (self, other))
 
73
 
 
74
    # Math ops
 
75
    def __add__(self, other):
 
76
        # Default behavior is to treat both sides like strings
 
77
        if isinstance(other, String):
 
78
            return String(self.render() + other.value, quotes=other.quotes)
 
79
        return String(self.render() + other.render())
 
80
 
 
81
    def __sub__(self, other):
 
82
        # Default behavior is to treat the whole expression like one string
 
83
        return String(self.render() + "-" + other.render())
 
84
 
 
85
    def __div__(self, other):
 
86
        return String(self.render() + "/" + other.render())
 
87
 
 
88
    # Sass types have no notion of floor vs true division
 
89
    def __truediv__(self, other):
 
90
        return self.__div__(other)
 
91
 
 
92
    def __floordiv__(self, other):
 
93
        return self.__div__(other)
 
94
 
 
95
    def __mul__(self, other):
 
96
        return NotImplemented
 
97
 
 
98
    def __pos__(self):
 
99
        return String("+" + self.render())
 
100
 
 
101
    def __neg__(self):
 
102
        return String("-" + self.render())
 
103
 
 
104
    def to_dict(self):
 
105
        """Return the Python dict equivalent of this map.
 
106
 
 
107
        If this type can't be expressed as a map, raise.
 
108
        """
 
109
        return dict(self.to_pairs())
 
110
 
 
111
    def to_pairs(self):
 
112
        """Return the Python list-of-tuples equivalent of this map.  Note that
 
113
        this is different from ``self.to_dict().items()``, because Sass maps
 
114
        preserve order.
 
115
 
 
116
        If this type can't be expressed as a map, raise.
 
117
        """
 
118
        raise ValueError("Not a map: {0!r}".format(self))
 
119
 
 
120
    def render(self, compress=False):
 
121
        return self.__str__()
 
122
 
 
123
 
 
124
class Null(Value):
 
125
    is_null = True
 
126
    sass_type_name = u'null'
 
127
 
 
128
    def __init__(self, value=None):
 
129
        pass
 
130
 
 
131
    def __str__(self):
 
132
        return self.sass_type_name
 
133
 
 
134
    def __repr__(self):
 
135
        return "<%s()>" % (self.__class__.__name__,)
 
136
 
 
137
    def __hash__(self):
 
138
        return hash(None)
 
139
 
 
140
    def __bool__(self):
 
141
        return False
 
142
 
 
143
    def __eq__(self, other):
 
144
        return Boolean(isinstance(other, Null))
 
145
 
 
146
    def __ne__(self, other):
 
147
        return Boolean(not self.__eq__(other))
 
148
 
 
149
    def render(self, compress=False):
 
150
        return self.sass_type_name
 
151
 
 
152
 
 
153
class Undefined(Null):
 
154
    sass_type_name = u'undefined'
 
155
 
 
156
    def __init__(self, value=None):
 
157
        pass
 
158
 
 
159
    def __add__(self, other):
 
160
        return self
 
161
 
 
162
    def __radd__(self, other):
 
163
        return self
 
164
 
 
165
    def __sub__(self, other):
 
166
        return self
 
167
 
 
168
    def __rsub__(self, other):
 
169
        return self
 
170
 
 
171
    def __div__(self, other):
 
172
        return self
 
173
 
 
174
    def __rdiv__(self, other):
 
175
        return self
 
176
 
 
177
    def __truediv__(self, other):
 
178
        return self
 
179
 
 
180
    def __rtruediv__(self, other):
 
181
        return self
 
182
 
 
183
    def __floordiv__(self, other):
 
184
        return self
 
185
 
 
186
    def __rfloordiv__(self, other):
 
187
        return self
 
188
 
 
189
    def __mul__(self, other):
 
190
        return self
 
191
 
 
192
    def __rmul__(self, other):
 
193
        return self
 
194
 
 
195
    def __pos__(self):
 
196
        return self
 
197
 
 
198
    def __neg__(self):
 
199
        return self
 
200
 
 
201
 
 
202
class Boolean(Value):
 
203
    sass_type_name = u'bool'
 
204
 
 
205
    def __init__(self, value):
 
206
        self.value = bool(value)
 
207
 
 
208
    def __str__(self):
 
209
        return 'true' if self.value else 'false'
 
210
 
 
211
    def __hash__(self):
 
212
        return hash(self.value)
 
213
 
 
214
    def __bool__(self):
 
215
        return self.value
 
216
 
 
217
    def render(self, compress=False):
 
218
        if self.value:
 
219
            return 'true'
 
220
        else:
 
221
            return 'false'
 
222
 
 
223
 
 
224
class Number(Value):
 
225
    sass_type_name = u'number'
 
226
 
 
227
    def __init__(self, amount, unit=None, unit_numer=(), unit_denom=()):
 
228
        if isinstance(amount, Number):
 
229
            assert not unit and not unit_numer and not unit_denom
 
230
            self.value = amount.value
 
231
            self.unit_numer = amount.unit_numer
 
232
            self.unit_denom = amount.unit_denom
 
233
            return
 
234
 
 
235
        if not isinstance(amount, (int, float)):
 
236
            raise TypeError("Expected number, got %r" % (amount,))
 
237
 
 
238
        if unit is not None:
 
239
            unit_numer = unit_numer + (unit.lower(),)
 
240
 
 
241
        # Cancel out any convertable units on the top and bottom
 
242
        numerator_base_units = count_base_units(unit_numer)
 
243
        denominator_base_units = count_base_units(unit_denom)
 
244
 
 
245
        # Count which base units appear both on top and bottom
 
246
        cancelable_base_units = {}
 
247
        for unit, count in numerator_base_units.items():
 
248
            cancelable_base_units[unit] = min(
 
249
                count, denominator_base_units.get(unit, 0))
 
250
 
 
251
        # Actually remove the units
 
252
        numer_factor, unit_numer = cancel_base_units(unit_numer, cancelable_base_units)
 
253
        denom_factor, unit_denom = cancel_base_units(unit_denom, cancelable_base_units)
 
254
 
 
255
        # And we're done
 
256
        self.unit_numer = tuple(unit_numer)
 
257
        self.unit_denom = tuple(unit_denom)
 
258
        self.value = amount * (numer_factor / denom_factor)
 
259
 
 
260
    def __repr__(self):
 
261
        full_unit = ' * '.join(self.unit_numer)
 
262
        if self.unit_denom:
 
263
            full_unit += ' / '
 
264
            full_unit += ' * '.join(self.unit_denom)
 
265
 
 
266
            if full_unit:
 
267
                full_unit = ' ' + full_unit
 
268
 
 
269
        return '<%s(%r%s)>' % (self.__class__.__name__, self.value, full_unit)
 
270
 
 
271
    def __hash__(self):
 
272
        return hash((self.value, self.unit_numer, self.unit_denom))
 
273
 
 
274
    def __int__(self):
 
275
        return int(self.value)
 
276
 
 
277
    def __float__(self):
 
278
        return float(self.value)
 
279
 
 
280
    def __pos__(self):
 
281
        return self
 
282
 
 
283
    def __neg__(self):
 
284
        return self * Number(-1)
 
285
 
 
286
    def __str__(self):
 
287
        return self.render()
 
288
 
 
289
    def __eq__(self, other):
 
290
        if not isinstance(other, Number):
 
291
            return Boolean(False)
 
292
        return self._compare(other, operator.__eq__, soft_fail=True)
 
293
 
 
294
    def __lt__(self, other):
 
295
        return self._compare(other, operator.__lt__)
 
296
 
 
297
    def __le__(self, other):
 
298
        return self._compare(other, operator.__le__)
 
299
 
 
300
    def __gt__(self, other):
 
301
        return self._compare(other, operator.__gt__)
 
302
 
 
303
    def __ge__(self, other):
 
304
        return self._compare(other, operator.__ge__)
 
305
 
 
306
    def _compare(self, other, op, soft_fail=False):
 
307
        if not isinstance(other, Number):
 
308
            raise TypeError("Can't compare %r and %r" % (self, other))
 
309
 
 
310
        # A unitless operand is treated as though it had the other operand's
 
311
        # units, and zero values can cast to anything, so in both cases the
 
312
        # units can be ignored
 
313
        if (self.is_unitless or other.is_unitless or
 
314
                self.value == 0 or other.value == 0):
 
315
            left = self
 
316
            right = other
 
317
        else:
 
318
            left = self.to_base_units()
 
319
            right = other.to_base_units()
 
320
 
 
321
            if left.unit_numer != right.unit_numer or left.unit_denom != right.unit_denom:
 
322
                if soft_fail:
 
323
                    # Used for equality only, where == should never fail
 
324
                    return Boolean(False)
 
325
                else:
 
326
                    raise ValueError("Can't reconcile units: %r and %r" % (self, other))
 
327
 
 
328
        return Boolean(op(round(left.value, 5), round(right.value, 5)))
 
329
 
 
330
    def __pow__(self, exp):
 
331
        if not isinstance(exp, Number):
 
332
            raise TypeError("Can't raise %r to power %r" % (self, exp))
 
333
        if not exp.is_unitless:
 
334
            raise TypeError("Exponent %r cannot have units" % (exp,))
 
335
 
 
336
        if self.is_unitless:
 
337
            return Number(self.value ** exp.value)
 
338
 
 
339
        # Units can only be exponentiated to integral powers -- what's the
 
340
        # square root of 'px'?  (Well, it's sqrt(px), but supporting that is
 
341
        # a bit out of scope.)
 
342
        if exp.value != int(exp.value):
 
343
            raise ValueError("Can't raise units of %r to non-integral power %r" % (self, exp))
 
344
 
 
345
        return Number(
 
346
            self.value ** int(exp.value),
 
347
            unit_numer=self.unit_numer * int(exp.value),
 
348
            unit_denom=self.unit_denom * int(exp.value),
 
349
        )
 
350
 
 
351
    def __mul__(self, other):
 
352
        if not isinstance(other, Number):
 
353
            return NotImplemented
 
354
 
 
355
        amount = self.value * other.value
 
356
        numer = self.unit_numer + other.unit_numer
 
357
        denom = self.unit_denom + other.unit_denom
 
358
 
 
359
        return Number(amount, unit_numer=numer, unit_denom=denom)
 
360
 
 
361
    def __div__(self, other):
 
362
        if not isinstance(other, Number):
 
363
            return NotImplemented
 
364
 
 
365
        amount = self.value / other.value
 
366
        numer = self.unit_numer + other.unit_denom
 
367
        denom = self.unit_denom + other.unit_numer
 
368
 
 
369
        return Number(amount, unit_numer=numer, unit_denom=denom)
 
370
 
 
371
    def __add__(self, other):
 
372
        # Numbers auto-cast to strings when added to other strings
 
373
        if isinstance(other, String):
 
374
            return String(self.render(), quotes=None) + other
 
375
 
 
376
        return self._add_sub(other, operator.add)
 
377
 
 
378
    def __sub__(self, other):
 
379
        return self._add_sub(other, operator.sub)
 
380
 
 
381
    def _add_sub(self, other, op):
 
382
        """Implements both addition and subtraction."""
 
383
        if not isinstance(other, Number):
 
384
            return NotImplemented
 
385
 
 
386
        # If either side is unitless, inherit the other side's units.  Skip all
 
387
        # the rest of the conversion math, too.
 
388
        if self.is_unitless or other.is_unitless:
 
389
            return Number(
 
390
                op(self.value, other.value),
 
391
                unit_numer=self.unit_numer or other.unit_numer,
 
392
                unit_denom=self.unit_denom or other.unit_denom,
 
393
            )
 
394
 
 
395
        # Likewise, if either side is zero, it can auto-cast to any units
 
396
        if self.value == 0:
 
397
            return Number(
 
398
                op(self.value, other.value),
 
399
                unit_numer=other.unit_numer,
 
400
                unit_denom=other.unit_denom,
 
401
            )
 
402
        elif other.value == 0:
 
403
            return Number(
 
404
                op(self.value, other.value),
 
405
                unit_numer=self.unit_numer,
 
406
                unit_denom=self.unit_denom,
 
407
            )
 
408
 
 
409
        # Reduce both operands to the same units
 
410
        left = self.to_base_units()
 
411
        right = other.to_base_units()
 
412
 
 
413
        if left.unit_numer != right.unit_numer or left.unit_denom != right.unit_denom:
 
414
            raise ValueError("Can't reconcile units: %r and %r" % (self, other))
 
415
 
 
416
        new_amount = op(left.value, right.value)
 
417
 
 
418
        # Convert back to the left side's units
 
419
        if left.value != 0:
 
420
            new_amount = new_amount * self.value / left.value
 
421
 
 
422
        return Number(new_amount, unit_numer=self.unit_numer, unit_denom=self.unit_denom)
 
423
 
 
424
    ### Helper methods, mostly used internally
 
425
 
 
426
    def to_base_units(self):
 
427
        """Convert to a fixed set of "base" units.  The particular units are
 
428
        arbitrary; what's important is that they're consistent.
 
429
 
 
430
        Used for addition and comparisons.
 
431
        """
 
432
        # Convert to "standard" units, as defined by the conversions dict above
 
433
        amount = self.value
 
434
 
 
435
        numer_factor, numer_units = convert_units_to_base_units(self.unit_numer)
 
436
        denom_factor, denom_units = convert_units_to_base_units(self.unit_denom)
 
437
 
 
438
        return Number(
 
439
            amount * numer_factor / denom_factor,
 
440
            unit_numer=numer_units,
 
441
            unit_denom=denom_units,
 
442
        )
 
443
 
 
444
    ### Utilities for public consumption
 
445
 
 
446
    @classmethod
 
447
    def wrap_python_function(cls, fn):
 
448
        """Wraps an unary Python math function, translating the argument from
 
449
        Sass to Python on the way in, and vice versa for the return value.
 
450
 
 
451
        Used to wrap simple Python functions like `ceil`, `floor`, etc.
 
452
        """
 
453
        def wrapped(sass_arg):
 
454
            # TODO enforce no units for trig?
 
455
            python_arg = sass_arg.value
 
456
            python_ret = fn(python_arg)
 
457
            sass_ret = cls(
 
458
                python_ret,
 
459
                unit_numer=sass_arg.unit_numer,
 
460
                unit_denom=sass_arg.unit_denom)
 
461
            return sass_ret
 
462
 
 
463
        return wrapped
 
464
 
 
465
    def to_python_index(self, length, check_bounds=True, circular=False):
 
466
        """Return a plain Python integer appropriate for indexing a sequence of
 
467
        the given length.  Raise if this is impossible for any reason
 
468
        whatsoever.
 
469
        """
 
470
        if not self.is_unitless:
 
471
            raise ValueError("Index cannot have units: {0!r}".format(self))
 
472
 
 
473
        ret = int(self.value)
 
474
        if ret != self.value:
 
475
            raise ValueError("Index must be an integer: {0!r}".format(ret))
 
476
 
 
477
        if ret == 0:
 
478
            raise ValueError("Index cannot be zero")
 
479
 
 
480
        if check_bounds and not circular and abs(ret) > length:
 
481
            raise ValueError("Index {0!r} out of bounds for length {1}".format(ret, length))
 
482
 
 
483
        if ret > 0:
 
484
            ret -= 1
 
485
 
 
486
        if circular:
 
487
            ret = ret % length
 
488
 
 
489
        return ret
 
490
 
 
491
    @property
 
492
    def has_simple_unit(self):
 
493
        """Returns True iff the unit is expressible in CSS, i.e., has no
 
494
        denominator and at most one unit in the numerator.
 
495
        """
 
496
        return len(self.unit_numer) <= 1 and not self.unit_denom
 
497
 
 
498
    def is_simple_unit(self, unit):
 
499
        """Return True iff the unit is simple (as above) and matches the given
 
500
        unit.
 
501
        """
 
502
        if self.unit_denom or len(self.unit_numer) > 1:
 
503
            return False
 
504
 
 
505
        if not self.unit_numer:
 
506
            # Empty string historically means no unit
 
507
            return unit == ''
 
508
 
 
509
        return self.unit_numer[0] == unit
 
510
 
 
511
    @property
 
512
    def is_unitless(self):
 
513
        return not self.unit_numer and not self.unit_denom
 
514
 
 
515
    def render(self, compress=False):
 
516
        if not self.has_simple_unit:
 
517
            raise ValueError("Can't express compound units in CSS: %r" % (self,))
 
518
 
 
519
        if self.unit_numer:
 
520
            unit = self.unit_numer[0]
 
521
        else:
 
522
            unit = ''
 
523
 
 
524
        value = self.value
 
525
        if compress and unit in ZEROABLE_UNITS and value == 0:
 
526
            return '0'
 
527
 
 
528
        if value == 0:  # -0.0 is plain 0
 
529
            value = 0
 
530
 
 
531
        val = "%0.05f" % round(value, 5)
 
532
        val = val.rstrip('0').rstrip('.')
 
533
 
 
534
        if compress and val.startswith('0.'):
 
535
            # Strip off leading zero when compressing
 
536
            val = val[1:]
 
537
 
 
538
        return val + unit
 
539
 
 
540
 
 
541
class List(Value):
 
542
    """A list of other values.  May be delimited by commas or spaces.
 
543
 
 
544
    Lists of one item don't make much sense in CSS, but can exist in Sass.  Use ......
 
545
 
 
546
    Lists may also contain zero items, but these are forbidden from appearing
 
547
    in CSS output.
 
548
    """
 
549
 
 
550
    sass_type_name = u'list'
 
551
 
 
552
    def __init__(self, iterable, separator=None, use_comma=None, is_literal=False):
 
553
        if isinstance(iterable, List):
 
554
            iterable = iterable.value
 
555
 
 
556
        if not isinstance(iterable, (list, tuple)):
 
557
            raise TypeError("Expected list, got %r" % (iterable,))
 
558
 
 
559
        self.value = list(iterable)
 
560
 
 
561
        for item in self.value:
 
562
            if not isinstance(item, Value):
 
563
                raise TypeError("Expected a Sass type, got %r" % (item,))
 
564
 
 
565
        # TODO remove separator argument entirely
 
566
        if use_comma is None:
 
567
            self.use_comma = separator == ","
 
568
        else:
 
569
            self.use_comma = use_comma
 
570
 
 
571
        self.is_literal = is_literal
 
572
 
 
573
    @classmethod
 
574
    def maybe_new(cls, values, use_comma=True):
 
575
        """If `values` contains only one item, return that item.  Otherwise,
 
576
        return a List as normal.
 
577
        """
 
578
        if len(values) == 1:
 
579
            return values[0]
 
580
        else:
 
581
            return cls(values, use_comma=use_comma)
 
582
 
 
583
    def maybe(self):
 
584
        """If this List contains only one item, return it.  Otherwise, return
 
585
        the List.
 
586
        """
 
587
        if len(self.value) == 1:
 
588
            return self.value[0]
 
589
        else:
 
590
            return self
 
591
 
 
592
    @classmethod
 
593
    def from_maybe(cls, values, use_comma=True):
 
594
        """If `values` appears to not be a list, return a list containing it.
 
595
        Otherwise, return a List as normal.
 
596
        """
 
597
        if values is None:
 
598
            values = []
 
599
        return values
 
600
 
 
601
    @classmethod
 
602
    def from_maybe_starargs(cls, args, use_comma=True):
 
603
        """If `args` has one element which appears to be a list, return it.
 
604
        Otherwise, return a list as normal.
 
605
 
 
606
        Mainly used by Sass function implementations that predate `...`
 
607
        support, so they can accept both a list of arguments and a single list
 
608
        stored in a variable.
 
609
        """
 
610
        if len(args) == 1:
 
611
            if isinstance(args[0], cls):
 
612
                return args[0]
 
613
            elif isinstance(args[0], (list, tuple)):
 
614
                return cls(args[0], use_comma=use_comma)
 
615
 
 
616
        return cls(args, use_comma=use_comma)
 
617
 
 
618
    def __repr__(self):
 
619
        return "<List(%r, %r)>" % (
 
620
            self.value,
 
621
            self.delimiter(compress=True),
 
622
        )
 
623
 
 
624
    def __hash__(self):
 
625
        return hash((tuple(self.value), self.use_comma))
 
626
 
 
627
    def delimiter(self, compress=False):
 
628
        if self.use_comma:
 
629
            if compress:
 
630
                return ','
 
631
            else:
 
632
                return ', '
 
633
        else:
 
634
            return ' '
 
635
 
 
636
    def __len__(self):
 
637
        return len(self.value)
 
638
 
 
639
    def __str__(self):
 
640
        return self.render()
 
641
 
 
642
    def __iter__(self):
 
643
        return iter(self.value)
 
644
 
 
645
    def __contains__(self, item):
 
646
        return item in self.value
 
647
 
 
648
    def __getitem__(self, key):
 
649
        return self.value[key]
 
650
 
 
651
    def to_pairs(self):
 
652
        pairs = []
 
653
        for item in self:
 
654
            if len(item) != 2:
 
655
                return super(List, self).to_pairs()
 
656
 
 
657
            pairs.append(tuple(item))
 
658
 
 
659
        return pairs
 
660
 
 
661
    def render(self, compress=False):
 
662
        if not self.value:
 
663
            raise ValueError("Can't render empty list as CSS")
 
664
 
 
665
        delim = self.delimiter(compress)
 
666
 
 
667
        if self.is_literal:
 
668
            value = self.value
 
669
        else:
 
670
            # Non-literal lists have nulls stripped
 
671
            value = [item for item in self.value if not item.is_null]
 
672
            # Non-empty lists containing only nulls become nothing, just like
 
673
            # single nulls
 
674
            if not value:
 
675
                return ''
 
676
 
 
677
        return delim.join(
 
678
            item.render(compress=compress)
 
679
            for item in value
 
680
        )
 
681
 
 
682
    # DEVIATION: binary ops on lists and scalars act element-wise
 
683
    def __add__(self, other):
 
684
        if isinstance(other, List):
 
685
            max_list, min_list = (self, other) if len(self) > len(other) else (other, self)
 
686
            return List([item + max_list[i] for i, item in enumerate(min_list)], use_comma=self.use_comma)
 
687
 
 
688
        elif isinstance(other, String):
 
689
            # UN-DEVIATION: adding a string should fall back to canonical
 
690
            # behavior of string addition
 
691
            return super(List, self).__add__(other)
 
692
 
 
693
        else:
 
694
            return List([item + other for item in self], use_comma=self.use_comma)
 
695
 
 
696
    def __sub__(self, other):
 
697
        if isinstance(other, List):
 
698
            max_list, min_list = (self, other) if len(self) > len(other) else (other, self)
 
699
            return List([item - max_list[i] for i, item in enumerate(min_list)], use_comma=self.use_comma)
 
700
 
 
701
        return List([item - other for item in self], use_comma=self.use_comma)
 
702
 
 
703
    def __mul__(self, other):
 
704
        if isinstance(other, List):
 
705
            max_list, min_list = (self, other) if len(self) > len(other) else (other, self)
 
706
            max_list, min_list = (self, other) if len(self) > len(other) else (other, self)
 
707
            return List([item * max_list[i] for i, item in enumerate(min_list)], use_comma=self.use_comma)
 
708
 
 
709
        return List([item * other for item in self], use_comma=self.use_comma)
 
710
 
 
711
    def __div__(self, other):
 
712
        if isinstance(other, List):
 
713
            max_list, min_list = (self, other) if len(self) > len(other) else (other, self)
 
714
            return List([item / max_list[i] for i, item in enumerate(min_list)], use_comma=self.use_comma)
 
715
 
 
716
        return List([item / other for item in self], use_comma=self.use_comma)
 
717
 
 
718
    def __pos__(self):
 
719
        return self
 
720
 
 
721
    def __neg__(self):
 
722
        return List([-item for item in self], use_comma=self.use_comma)
 
723
 
 
724
 
 
725
def _constrain(value, lb=0, ub=1):
 
726
    """Helper for Color constructors.  Constrains a value to a range."""
 
727
    if value < lb:
 
728
        return lb
 
729
    elif value > ub:
 
730
        return ub
 
731
    else:
 
732
        return value
 
733
 
 
734
 
 
735
class Color(Value):
 
736
    sass_type_name = u'color'
 
737
    original_literal = None
 
738
 
 
739
    def __init__(self, tokens):
 
740
        self.tokens = tokens
 
741
        self.value = (0, 0, 0, 1)
 
742
        if tokens is None:
 
743
            self.value = (0, 0, 0, 1)
 
744
        elif isinstance(tokens, Color):
 
745
            self.value = tokens.value
 
746
        else:
 
747
            raise TypeError("Can't make Color from %r" % (tokens,))
 
748
 
 
749
    ### Alternate constructors
 
750
 
 
751
    @classmethod
 
752
    def from_rgb(cls, red, green, blue, alpha=1.0, original_literal=None):
 
753
        red = _constrain(red)
 
754
        green = _constrain(green)
 
755
        blue = _constrain(blue)
 
756
        alpha = _constrain(alpha)
 
757
 
 
758
        self = cls.__new__(cls)  # TODO
 
759
        self.tokens = None
 
760
        # TODO really should store these things internally as 0-1, but can't
 
761
        # until stuff stops examining .value directly
 
762
        self.value = (red * 255.0, green * 255.0, blue * 255.0, alpha)
 
763
 
 
764
        if original_literal is not None:
 
765
            self.original_literal = original_literal
 
766
 
 
767
        return self
 
768
 
 
769
    @classmethod
 
770
    def from_hsl(cls, hue, saturation, lightness, alpha=1.0):
 
771
        hue = _constrain(hue)
 
772
        saturation = _constrain(saturation)
 
773
        lightness = _constrain(lightness)
 
774
        alpha = _constrain(alpha)
 
775
 
 
776
        r, g, b = colorsys.hls_to_rgb(hue, lightness, saturation)
 
777
        return cls.from_rgb(r, g, b, alpha)
 
778
 
 
779
    @classmethod
 
780
    def from_hex(cls, hex_string, literal=False):
 
781
        if not hex_string.startswith('#'):
 
782
            raise ValueError("Expected #abcdef, got %r" % (hex_string,))
 
783
 
 
784
        if literal:
 
785
            original_literal = hex_string
 
786
        else:
 
787
            original_literal = None
 
788
 
 
789
        hex_string = hex_string[1:]
 
790
 
 
791
        # Always include the alpha channel
 
792
        if len(hex_string) == 3:
 
793
            hex_string += 'f'
 
794
        elif len(hex_string) == 6:
 
795
            hex_string += 'ff'
 
796
 
 
797
        # Now there should be only two possibilities.  Normalize to a list of
 
798
        # two hex digits
 
799
        if len(hex_string) == 4:
 
800
            chunks = [ch * 2 for ch in hex_string]
 
801
        elif len(hex_string) == 8:
 
802
            chunks = [
 
803
                hex_string[0:2], hex_string[2:4], hex_string[4:6], hex_string[6:8]
 
804
            ]
 
805
 
 
806
        rgba = [int(ch, 16) / 255 for ch in chunks]
 
807
        return cls.from_rgb(*rgba, original_literal=original_literal)
 
808
 
 
809
    @classmethod
 
810
    def from_name(cls, name):
 
811
        """Build a Color from a CSS color name."""
 
812
        self = cls.__new__(cls)  # TODO
 
813
        self.original_literal = name
 
814
 
 
815
        r, g, b, a = COLOR_NAMES[name]
 
816
 
 
817
        self.value = r, g, b, a
 
818
        return self
 
819
 
 
820
    ### Accessors
 
821
 
 
822
    @property
 
823
    def rgb(self):
 
824
        # TODO: deprecate, relies on internals
 
825
        return tuple(self.value[:3])
 
826
 
 
827
    @property
 
828
    def rgba(self):
 
829
        return (
 
830
            self.value[0] / 255,
 
831
            self.value[1] / 255,
 
832
            self.value[2] / 255,
 
833
            self.value[3],
 
834
        )
 
835
 
 
836
    @property
 
837
    def hsl(self):
 
838
        rgba = self.rgba
 
839
        h, l, s = colorsys.rgb_to_hls(*rgba[:3])
 
840
        return h, s, l
 
841
 
 
842
    @property
 
843
    def alpha(self):
 
844
        return self.value[3]
 
845
 
 
846
    @property
 
847
    def rgba255(self):
 
848
        return (
 
849
            int(self.value[0] * 1 + 0.5),
 
850
            int(self.value[1] * 1 + 0.5),
 
851
            int(self.value[2] * 1 + 0.5),
 
852
            int(self.value[3] * 255 + 0.5),
 
853
        )
 
854
 
 
855
    def __repr__(self):
 
856
        return '<%s(%s)>' % (self.__class__.__name__, repr(self.value))
 
857
 
 
858
    def __hash__(self):
 
859
        return hash(self.value)
 
860
 
 
861
    def __eq__(self, other):
 
862
        if not isinstance(other, Color):
 
863
            return Boolean(False)
 
864
 
 
865
        # Scale channels to 255 and round to integers; this allows only 8-bit
 
866
        # color, but Ruby sass makes the same assumption, and otherwise it's
 
867
        # easy to get lots of float errors for HSL colors.
 
868
        left = tuple(round(n) for n in self.rgba255)
 
869
        right = tuple(round(n) for n in other.rgba255)
 
870
        return Boolean(left == right)
 
871
 
 
872
    def __add__(self, other):
 
873
        if isinstance(other, (Color, Number)):
 
874
            return self._operate(other, operator.add)
 
875
        else:
 
876
            return super(Color, self).__add__(other)
 
877
 
 
878
    def __sub__(self, other):
 
879
        if isinstance(other, (Color, Number)):
 
880
            return self._operate(other, operator.sub)
 
881
        else:
 
882
            return super(Color, self).__sub__(other)
 
883
 
 
884
    def __mul__(self, other):
 
885
        if isinstance(other, (Color, Number)):
 
886
            return self._operate(other, operator.mul)
 
887
        else:
 
888
            return super(Color, self).__mul__(other)
 
889
 
 
890
    def __div__(self, other):
 
891
        if isinstance(other, (Color, Number)):
 
892
            return self._operate(other, operator.div)
 
893
        else:
 
894
            return super(Color, self).__div__(other)
 
895
 
 
896
    def _operate(self, other, op):
 
897
        if isinstance(other, Number):
 
898
            if not other.is_unitless:
 
899
                raise ValueError("Expected unitless Number, got %r" % (other,))
 
900
 
 
901
            other_rgb = (other.value,) * 3
 
902
        elif isinstance(other, Color):
 
903
            if self.alpha != other.alpha:
 
904
                raise ValueError("Alpha channels must match between %r and %r"
 
905
                    % (self, other))
 
906
 
 
907
            other_rgb = other.rgb
 
908
        else:
 
909
            raise TypeError("Expected Color or Number, got %r" % (other,))
 
910
 
 
911
        new_rgb = [
 
912
            min(255., max(0., op(left, right)))
 
913
            # for from_rgb
 
914
                / 255.
 
915
            for (left, right) in zip(self.rgb, other_rgb)
 
916
        ]
 
917
 
 
918
        return Color.from_rgb(*new_rgb, alpha=self.alpha)
 
919
 
 
920
    def render(self, compress=False):
 
921
        """Return a rendered representation of the color.  If `compress` is
 
922
        true, the shortest possible representation is used; otherwise, named
 
923
        colors are rendered as names and all others are rendered as hex (or
 
924
        with the rgba function).
 
925
        """
 
926
 
 
927
        if not compress and self.original_literal:
 
928
            return self.original_literal
 
929
 
 
930
        candidates = []
 
931
 
 
932
        # TODO this assumes CSS resolution is 8-bit per channel, but so does
 
933
        # Ruby.
 
934
        r, g, b, a = self.value
 
935
        r, g, b = int(round(r)), int(round(g)), int(round(b))
 
936
 
 
937
        # Build a candidate list in order of preference.  If `compress` is
 
938
        # True, the shortest candidate is used; otherwise, the first candidate
 
939
        # is used.
 
940
 
 
941
        # Try color name
 
942
        key = r, g, b, a
 
943
        if key in COLOR_LOOKUP:
 
944
            candidates.append(COLOR_LOOKUP[key])
 
945
 
 
946
        if a == 1:
 
947
            # Hex is always shorter than function notation
 
948
            if all(ch % 17 == 0 for ch in (r, g, b)):
 
949
                candidates.append("#%1x%1x%1x" % (r // 17, g // 17, b // 17))
 
950
            else:
 
951
                candidates.append("#%02x%02x%02x" % (r, g, b))
 
952
        else:
 
953
            # Can't use hex notation for RGBA
 
954
            if compress:
 
955
                sp = ''
 
956
            else:
 
957
                sp = ' '
 
958
            candidates.append("rgba(%d,%s%d,%s%d,%s%.2g)" % (r, sp, g, sp, b, sp, a))
 
959
 
 
960
        if compress:
 
961
            return min(candidates, key=len)
 
962
        else:
 
963
            return candidates[0]
 
964
 
 
965
 
 
966
# TODO be unicode-clean and delete this nonsense
 
967
DEFAULT_STRING_ENCODING = "utf8"
 
968
 
 
969
 
 
970
class String(Value):
 
971
    """Represents both CSS quoted string values and CSS identifiers (such as
 
972
    `left`).
 
973
 
 
974
    Makes no distinction between single and double quotes, except that the same
 
975
    quotes are preserved on string literals that pass through unmodified.
 
976
    Otherwise, double quotes are used.
 
977
    """
 
978
 
 
979
    sass_type_name = u'string'
 
980
 
 
981
    def __init__(self, value, quotes='"'):
 
982
        if isinstance(value, String):
 
983
            # TODO unclear if this should be here, but many functions rely on
 
984
            # it
 
985
            value = value.value
 
986
        elif isinstance(value, Number):
 
987
            # TODO this may only be necessary in the case of __radd__ and
 
988
            # number values
 
989
            value = str(value)
 
990
 
 
991
        if isinstance(value, six.binary_type):
 
992
            value = value.decode(DEFAULT_STRING_ENCODING)
 
993
 
 
994
        if not isinstance(value, six.text_type):
 
995
            raise TypeError("Expected string, got {0!r}".format(value))
 
996
 
 
997
        # TODO probably disallow creating an unquoted string outside a
 
998
        # set of chars like [-a-zA-Z0-9]+
 
999
 
 
1000
        if six.PY3:
 
1001
            self.value = value
 
1002
        else:
 
1003
            # TODO well, at least 3 uses unicode everywhere
 
1004
            self.value = value.encode(DEFAULT_STRING_ENCODING)
 
1005
        self.quotes = quotes
 
1006
 
 
1007
    @classmethod
 
1008
    def unquoted(cls, value):
 
1009
        """Helper to create a string with no quotes."""
 
1010
        return cls(value, quotes=None)
 
1011
 
 
1012
    def __hash__(self):
 
1013
        return hash(self.value)
 
1014
 
 
1015
    def __str__(self):
 
1016
        if self.quotes:
 
1017
            return self.quotes + escape(self.value) + self.quotes
 
1018
        else:
 
1019
            return self.value
 
1020
 
 
1021
    def __repr__(self):
 
1022
        if self.quotes != '"':
 
1023
            quotes = ', quotes=%r' % self.quotes
 
1024
        else:
 
1025
            quotes = ''
 
1026
        return '<%s(%s%s)>' % (self.__class__.__name__, repr(self.value), quotes)
 
1027
 
 
1028
    def __eq__(self, other):
 
1029
        return Boolean(isinstance(other, String) and self.value == other.value)
 
1030
 
 
1031
    def __add__(self, other):
 
1032
        if isinstance(other, String):
 
1033
            other_value = other.value
 
1034
        else:
 
1035
            other_value = other.render()
 
1036
 
 
1037
        return String(
 
1038
            self.value + other_value,
 
1039
            quotes='"' if self.quotes else None)
 
1040
 
 
1041
    def __mul__(self, other):
 
1042
        # DEVIATION: Ruby Sass doesn't do this, because Ruby doesn't.  But
 
1043
        # Python does, and in Ruby Sass it's just fatal anyway.
 
1044
        if not isinstance(other, Number):
 
1045
            return super(String, self).__mul__(other)
 
1046
 
 
1047
        if not other.is_unitless:
 
1048
            raise TypeError("Can only multiply strings by unitless numbers")
 
1049
 
 
1050
        n = other.value
 
1051
        if n != int(n):
 
1052
            raise ValueError("Can only multiply strings by integers")
 
1053
 
 
1054
        return String(self.value * int(other.value), quotes=self.quotes)
 
1055
 
 
1056
    def render(self, compress=False):
 
1057
        return self.__str__()
 
1058
 
 
1059
 
 
1060
class Map(Value):
 
1061
    sass_type_name = u'map'
 
1062
 
 
1063
    def __init__(self, pairs, index=None):
 
1064
        self.pairs = tuple(pairs)
 
1065
 
 
1066
        if index is None:
 
1067
            self.index = {}
 
1068
            for key, value in pairs:
 
1069
                self.index[key] = value
 
1070
        else:
 
1071
            self.index = index
 
1072
 
 
1073
    def __repr__(self):
 
1074
        return "<Map: (%s)>" % (", ".join("%s: %s" % pair for pair in self.pairs),)
 
1075
 
 
1076
    def __hash__(self):
 
1077
        return hash(self.pairs)
 
1078
 
 
1079
    def __len__(self):
 
1080
        return len(self.pairs)
 
1081
 
 
1082
    def __iter__(self):
 
1083
        return iter(self.pairs)
 
1084
 
 
1085
    def __getitem__(self, index):
 
1086
        return List(self.pairs[index], use_comma=True)
 
1087
 
 
1088
    def __eq__(self, other):
 
1089
        try:
 
1090
            return self.pairs == other.to_pairs()
 
1091
        except ValueError:
 
1092
            return NotImplemented
 
1093
 
 
1094
    def to_dict(self):
 
1095
        return self.index
 
1096
 
 
1097
    def to_pairs(self):
 
1098
        return self.pairs
 
1099
 
 
1100
    def render(self, compress=False):
 
1101
        raise TypeError("Cannot render map %r as CSS" % (self,))
 
1102
 
 
1103
 
 
1104
def expect_type(value, types, unit=any):
 
1105
    if not isinstance(value, types):
 
1106
        if isinstance(types, type):
 
1107
            types = (type,)
 
1108
        sass_type_names = list(set(t.sass_type_name for t in types))
 
1109
        sass_type_names.sort()
 
1110
 
 
1111
        # Join with commas in English fashion
 
1112
        if len(sass_type_names) == 1:
 
1113
            sass_type = sass_type_names[0]
 
1114
        elif len(sass_type_names) == 2:
 
1115
            sass_type = u' or '.join(sass_type_names)
 
1116
        else:
 
1117
            sass_type = u', '.join(sass_type_names[:-1])
 
1118
            sass_type += u', or ' + sass_type_names[-1]
 
1119
 
 
1120
        raise TypeError("Expected %s, got %r" % (sass_type, value))
 
1121
 
 
1122
    if unit is not any and isinstance(value, Number):
 
1123
        if unit is None and not value.is_unitless:
 
1124
            raise ValueError("Expected unitless number, got %r" % (value,))
 
1125
 
 
1126
        elif unit == '%' and not (
 
1127
                value.is_unitless or value.is_simple_unit('%')):
 
1128
            raise ValueError("Expected unitless number or percentage, got %r" % (value,))