def test_ceiling_function(): for arg, expected in ( (string_literal('"3.14"'), datatypes.number(4)), (NOT_A_NUMBER, datatypes.NOT_A_NUMBER), (POSITIVE_INFINITY, datatypes.POSITIVE_INFINITY), (number_literal('0.5'), datatypes.number(1)), (number_literal('-0.5'), datatypes.number(-0.0)), ): result = function_call('ceiling', [arg]).evaluate_as_number(CONTEXT1) assert isinstance(result, datatypes.number) assert ((result.isnan() and expected.isnan()) or result == expected), (result, expected)
def assertEquals(self, first, second, msg=None): if msg is None: msg = '%r == %r' % (first, second) # convert nan's to strings to prevent IEEE 754-style compares if isinstance(first, float) and isinstance(second, float): if datatypes.number(first).isnan(): first = 'NaN' if datatypes.number(second).isnan(): second = 'NaN' # convert nodesets to lists to prevent XPath-style nodeset compares elif isinstance(first, list) and isinstance(second, list): first, second = list(first), list(second) return test_case.assertEquals(self, first, second, msg)
def test_round_function(): for arg, expected in ( (string_literal('"3.14"'), datatypes.number(3)), (number_literal('-4.5'), datatypes.number(-4)), (NOT_A_NUMBER, datatypes.NOT_A_NUMBER), (POSITIVE_INFINITY, datatypes.POSITIVE_INFINITY), (NEGATIVE_INFINITY, datatypes.NEGATIVE_INFINITY), (string_literal('"12345"'), datatypes.number(12345)), ): print "Call", arg result = function_call('round', [arg]).evaluate_as_number(CONTEXT1) assert isinstance(result, datatypes.number) assert ((result.isnan() and expected.isnan()) or result == expected), (result, expected)
def tan_function(context, number): """ The math:tan function returns the tangent of the number passed as an argument. """ result = math.tan(number.evaluate_as_number(context)) return datatypes.number(result)
def exp_function(context, number): """ The math:exp function returns e (the base of natural logarithms) raised to a power. """ result = math.exp(number.evaluate_as_number(context)) return datatypes.number(result)
def evaluate_as_number(self, context): arg0, = self._args arg0 = arg0.evaluate_as_number(context) # a "normal" number is neither zero, NaN, nor infinity if arg0.isnormal(): return datatypes.number(math.ceil(arg0)) return arg0
def test_string_length_function(): result = function_call( 'string-length', [string_literal('"3.14Hi"')]).evaluate_as_number(CONTEXT1) assert isinstance(result, datatypes.number) expected = datatypes.number(6) assert result == expected, (result, expected)
def compile_as_number(self, compiler): try: value = datatypes.number(self._literal) except ValueError: value = datatypes.NOT_A_NUMBER compiler.emit('LOAD_CONST', value) return
def evaluate_as_number(self, context): arg0, = self._args if arg0 is None: string = datatypes.string(context.node) else: string = arg0.evaluate_as_string(context) return datatypes.number(len(string))
def compile_as_number(self, compiler): try: value = datatypes.number(self._literal) except ValueError: value = datatypes.number.NaN compiler.emit('LOAD_CONST', value) return
def atan_function(context, number): """ The math:atan function returns the arctangent value of a number. """ try: result = math.atan(number.evaluate_as_number(context)) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number(result)
def acos_function(context, number): """ The math:acos function returns the arccosine value of a number. """ try: result = math.acos(number.evaluate_as_number(context)) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number(result)
def constant_function(context, name, precision): """ The math:constant function returns the specified constant to a set precision. """ name = nam.evaluate_as_string(context) precision = precision.evaluate_as_number(context) try: result = _named_constants[name] except KeyError: return datatypes.NOT_A_NUMBER return datatypes.number('%0.*f' % (int(precision), result))
def minute_in_hour_function(context, time=None): """ The date:minute-in-hour function returns the minute portion of a date-time string as an integer. Implements version 2. """ try: datetime = _coerce(context, time, ('dateTime', 'time')) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number(datetime.minute)
def evaluate_as_number(self, context): arg0, = self._args arg0 = arg0.evaluate_as_number(context) # a "normal" number is neither zero, NaN, nor infinity if arg0.isnormal(): # Round towards positive infinity when there are two possibilities if arg0 % 1.0 == 0.5: round = math.ceil else: round = math.floor return datatypes.number(round(arg0)) return arg0
def second_in_minute_function(context, time=None): """ The date:second-in-minute function returns the seconds portion of a date-time string as an integer. Implements version 2. """ try: datetime = _coerce(context, time, ('dateTime', 'time')) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number(datetime.second)
def hour_in_day_function(context, time=None): """ The date:hour-in-date function returns the hour portion of a date- time string as an integer. Implements version 2. """ try: datetime = _coerce(context, time, ('dateTime', 'time')) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number(datetime.hour)
def atan2_function(context, y, x): """ The math:atan2 function returns the angle ( in radians ) from the X axis to a point (y,x). """ x = x.evaluate_as_number(context) y = y.evaluate_as_number(context) try: result = math.atan2(y, x) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number(result)
def year_function(context, date=None): """ The date:year function returns the year portion of the dateTime supplied, or of the current year, as an integer. Implements version 2. """ try: datetime = _coerce(context, date, ('dateTime', 'date', 'gYearMonth', 'gYear')) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number(datetime.year)
def month_in_year_function(context, date=None): """ The date:month-in-year function returns the month portion of the dateTime argument (defaults to current month) as an integer. Implements version 2. """ try: datetime = _coerce(context, date, ('dateTime', 'date', 'gYearMonth', 'gMonthDay', 'gMonth')) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number(datetime.month)
def day_in_month_function(context, date=None): """ The date:day-in-month function returns the numerical date, i.e. 27 for the 27th of March. Implements version 2. """ try: datetime = _coerce(context, date, ('dateTime', 'date', 'gMonthDay', 'gDay')) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number(datetime.day)
def day_in_year_function(context, date=None): """ The date:day-in-year function returns a number representing the position of a date in the year. Implements version 2. """ try: datetime = _coerce(context, date, ('dateTime', 'date')) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number( _day_in_year(datetime.year, datetime.month, datetime.day))
def paramvalue(obj): """ Try to convert a Python object into an XPath data model value returns the value if successful, else None """ if isinstance(obj, datatypes.xpathobject): return obj if isinstance(obj, unicode): return datatypes.string(obj) elif isinstance(obj, str): try: obj = obj.decode("utf-8") except UnicodeError: return None else: return datatypes.string(obj) elif isinstance(obj, bool): # <bool> is subclasses of <int>, test first return datatypes.TRUE if obj else datatypes.FALSE elif isinstance(obj, (int, long, float)): return datatypes.number(obj) elif isinstance(obj, tree.node): return obj # NOTE: At one time (WSGI.xml days) this attemped to be smart and handle # all iterables but this would mean blindly dealing with dangerous # creatures, such as sockets. So now it's more conservative and sticks to # just list & tuple. elif isinstance(obj, (list, tuple)): # We can only use the list if the items are all nodes or all strings. # Strings are converted to a nodeset of text nodes. for item in obj: if not isinstance(item, (str, unicode)): break else: # We need to use an entity to preserve ordering entity = tree.entity() for item in obj: if isinstance(item, str): try: item = unicode(item, "utf8") except UnicodeError: return None entity.xml_append(tree.text(item)) return datatypes.nodeset(entity.xml_children) # We can only use the list if all the items are nodes. for item in obj: if not isinstance(item, tree.node): return None return datatypes.nodeset(obj) else: return None
def paramvalue(obj): """ Try to convert a Python object into an XPath data model value returns the value if successful, else None """ if isinstance(obj, datatypes.xpathobject): return obj if isinstance(obj, unicode): return datatypes.string(obj) elif isinstance(obj, str): try: obj = obj.decode('utf-8') except UnicodeError: return None else: return datatypes.string(obj) elif isinstance(obj, bool): # <bool> is subclasses of <int>, test first return datatypes.TRUE if obj else datatypes.FALSE elif isinstance(obj, (int, long, float)): return datatypes.number(obj) elif isinstance(obj, tree.node): return obj # NOTE: At one time (WSGI.xml days) this attemped to be smart and handle # all iterables but this would mean blindly dealing with dangerous # creatures, such as sockets. So now it's more conservative and sticks to # just list & tuple. elif isinstance(obj, (list, tuple)): # We can only use the list if the items are all nodes or all strings. # Strings are converted to a nodeset of text nodes. for item in obj: if not isinstance(item, (str, unicode)): break else: # We need to use an entity to preserve ordering entity = tree.entity() for item in obj: if isinstance(item, str): try: item = unicode(item, 'utf8') except UnicodeError: return None entity.xml_append(tree.text(item)) return datatypes.nodeset(entity.xml_children) # We can only use the list if all the items are nodes. for item in obj: if not isinstance(item, tree.node): return None return datatypes.nodeset(obj) else: return None
def day_in_week_function(context, date=None): """ The date:day-in-week function returns a number representing the weekday of a given date. Sunday is 1, Saturday is 7. Implements version 2. """ try: datetime = _coerce(context, date, ('dateTime', 'date')) except ValueError: return datatypes.NOT_A_NUMBER # `_day_of_week()` is zero-based Sunday, EXSLT needs 1-based result = _day_of_week(datetime.year, datetime.month, datetime.day) + 1 return datatypes.number(result)
def month_in_year_function(context, date=None): """ The date:month-in-year function returns the month portion of the dateTime argument (defaults to current month) as an integer. Implements version 2. """ try: datetime = _coerce( context, date, ('dateTime', 'date', 'gYearMonth', 'gMonthDay', 'gMonth')) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number(datetime.month)
def day_in_year_function(context, date=None): """ The date:day-in-year function returns a number representing the position of a date in the year. Implements version 2. """ try: datetime = _coerce(context, date, ('dateTime', 'date')) except ValueError: return datatypes.NOT_A_NUMBER return datatypes.number(_day_in_year(datetime.year, datetime.month, datetime.day))
def day_of_week_in_month_function(context, date=None): """ The date:day-of-week-in-month function returns the day-of-the-week in a month of a date as a number (e.g. 3 for the 3rd Tuesday in May). Implements version 2. """ try: datetime = _coerce(context, date, ('dateTime', 'date')) except ValueError: return datatypes.NOT_A_NUMBER # Note, using floor divison (//) to aid with `2to3` conversion result = ((datetime.day - 1) // 7) + 1 return datatypes.number(result)
def evaluate(self, context): arg0, = self._args arg0 = arg0.evaluate_as_string(context) namespace, property = context.expand_qname(arg0) if namespace == XSL_NAMESPACE: if property == 'version': return datatypes.number(1) elif property == 'vender': return datatypes.string('Amara') elif property == 'vender-url': return datatypes.string('http://hg.4suite.org/amara') elif namespace == EXTENSION_NAMESPACE: if property == 'version': return datatypes.string(__version__) elif property == 'platform': return datatypes.string(sys.platform) elif property == 'tempdir': raise elif namespace == 'http://xmlns.4suite.org/xslt/env-system-property': raise return datatypes.EMPTY_STRING
def week_in_year_function(context, date=None): """ The date:week-in-year function returns a number representing the week of the year a date is in. Implements version 3. """ # Notes: # - ISO 8601 specifies that Week 01 of the year is the week containing # the first Thursday; try: datetime = _coerce(context, date, ('dateTime', 'date')) except ValueError: return datatypes.NOT_A_NUMBER year, month, day = datetime.year, datetime.month, datetime.day # Find Jan 1 weekday for Y # _dayOfWeek returns 0=Sun, we need Mon=0 day_of_week_0101 = (_day_of_week(year, 1, 1) + 6) % 7 # Find weekday for Y M D day_number = _day_in_year(year, month, day) day_of_week = (day_number + day_of_week_0101 - 1) % 7 # Find if Y M D falls in year Y-1, week 52 or 53 # (i.e., the first 3 days of the year and DOW is Fri, Sat or Sun) if day_of_week_0101 > 3 and day_number <= (7 - day_of_week_0101): week = 52 + (day_of_week_0101 == (4 + _is_leap(year - 1))) # Find if Y M D falls in Y+1, week 1 # (i.e., the last 3 days of the year and DOW is Mon, Tue, or Wed) elif (365 + _is_leap(year) - day_number) < (3 - day_of_week): week = 1 else: week = (day_number + (6 - day_of_week) + day_of_week_0101) / 7 if day_of_week_0101 > 3: week -= 1 return datatypes.number(week)
def sqrt_function(context, number): """ The math:sqrt function returns the square root of a number. """ # The platform C library determines what math.sqrt() returns. # On some platforms, especially prior to Python 2.4, # nan may be returned for a negative or nan argument. # On other platforms, and especially since Python 2.4, # a ValueError is raised. # # EXSLT requires that we return zero for negative arg. # The result for a nan arg is undefined, but we'll return nan. number = number.evaluate_as_number(context) if number.isnan(): return number if n < 0.0: result = 0.0 else: try: result = math.sqrt(number) except ValueError: result = 0.0 return datatypes.number(result)
def test_parser_pass(): test_cases = [ (['child::*'], datatypes.nodeset(CHILDREN), CONTEXT_ROOT), (['/child::*'], datatypes.nodeset([ROOT]), CONTEXT_CHILD1), (['/*/*'], datatypes.nodeset(CHILDREN), CONTEXT_CHILD1), (['/child::*/*/child::GCHILD'], datatypes.nodeset(GCHILDREN1 + GCHILDREN2), CONTEXT_CHILD1), (['//*'], datatypes.nodeset([ROOT, CHILD1] + GCHILDREN1 + [CHILD2] + GCHILDREN2 + [CHILD3, LANG] + LCHILDREN), CONTEXT_CHILD1), (['//GCHILD'], datatypes.nodeset(GCHILDREN1 + GCHILDREN2), CONTEXT_CHILD1), (['//@attr1'], datatypes.nodeset([ATTR1, ATTR2]), CONTEXT_CHILD1), (['x:GCHILD'], datatypes.nodeset(), CONTEXT_CHILD1), (['.//GCHILD'], datatypes.nodeset(GCHILDREN2), CONTEXT_CHILD2), (['.//GCHILD'], datatypes.nodeset(GCHILDREN1 + GCHILDREN2), CONTEXT_ROOT), (['/'], datatypes.nodeset([DOC]), CONTEXT_TEXT), (['//CHILD1/..'], datatypes.nodeset([ROOT]), CONTEXT_CHILD1), (['CHILD1 | CHILD2'], datatypes.nodeset([CHILD1, CHILD2]), CONTEXT_ROOT), (['descendant::GCHILD[3]'], datatypes.nodeset([GCHILD21]), CONTEXT_ROOT), (['descendant::GCHILD[parent::CHILD1]'], datatypes.nodeset(GCHILDREN1), CONTEXT_ROOT), (['descendant::GCHILD[position() > 1]'], datatypes.nodeset([GCHILD12] + GCHILDREN2), CONTEXT_ROOT), (['CHILD2/@CODE'], datatypes.nodeset([IDATTR2]), CONTEXT_ROOT), (['CHILD2/@CODE * 0'], datatypes.number(0), CONTEXT_ROOT), ([u'f\xf6\xf8'], datatypes.nodeset([NONASCIIQNAME]), CONTEXT_LANG), (['@attr1[.="val1"]'], datatypes.nodeset([ATTR1]), CONTEXT_CHILD1), (["processing-instruction()"], datatypes.nodeset([PI, PI2]), CONTEXT_DOC), (["processing-instruction('no-data')"], datatypes.nodeset([PI2]), CONTEXT_DOC), (["processing-instruction('f')"], datatypes.nodeset(), CONTEXT_DOC), (['1'], datatypes.number(1), CONTEXT_ROOT), (['00200'], datatypes.number(200), CONTEXT_ROOT), (['3+4*7'], datatypes.number(31), CONTEXT_ROOT), (['3-4*1'], datatypes.number(-1), CONTEXT_ROOT), (['. * 0'], datatypes.NOT_A_NUMBER, CONTEXT_CHILD1), (['.. * 1'], datatypes.NOT_A_NUMBER, CONTEXT_CHILD1), #(['@attr31 * 1'], datatypes.NOT_A_NUMBER, CONTEXT_CHILD1), # TODO: Why is this commented out? (["string('1')"], datatypes.string("1"), CONTEXT_ROOT), (["concat('1', '2')"], datatypes.string("12"), CONTEXT_ROOT), (['true()'], datatypes.TRUE, CONTEXT_ROOT), (['false()'], datatypes.FALSE, CONTEXT_ROOT), (['1=3<4'], datatypes.TRUE, CONTEXT_ROOT), (['1 or 2 and 3'], datatypes.TRUE, CONTEXT_ROOT), (['1 and 2 = 3'], datatypes.FALSE, CONTEXT_ROOT), (['-1 or 2'], datatypes.TRUE, CONTEXT_ROOT), (['. or *'], datatypes.TRUE, CONTEXT_ROOT), (['$foo[1]'], datatypes.nodeset([ROOT]), CONTEXT_ROOT), (['$foo[1]/CHILD1'], datatypes.nodeset([CHILD1]), CONTEXT_ROOT), (['$foo[1]//GCHILD'], datatypes.nodeset(GCHILDREN1 + GCHILDREN2), CONTEXT_ROOT), (['$foo[1][3]'], datatypes.nodeset(), CONTEXT_ROOT), (['(child::*)'], datatypes.nodeset(CHILDREN), CONTEXT_ROOT), (['//element[descendant::y[.="z"]]'], datatypes.nodeset([ELEMENT2]), CONTEXT_ELEMENT), (['//element[descendant::y[.="z"]][1]'], datatypes.nodeset([ELEMENT2]), CONTEXT_ELEMENT), (['//element[descendant::y[.="z"]][2]'], datatypes.nodeset(), CONTEXT_ELEMENT), (["text()"], datatypes.nodeset(), CONTEXT_CHILD3), (["text()"], datatypes.nodeset([TEXT_WS1, TEXT_WS2, TEXT1]), CONTEXT_CHILD1), ] _run_parser_pass(test_cases)
def cos_function(context, number): """ The math:cos function returns cosine of the passed argument. """ result = math.cos(number.evaluate_as_number(context)) return datatypes.number(result)
def test_count_function(): node = function_call('count', (nodeset_literal([ROOT, CHILD1]), )) result = node.evaluate_as_number(CONTEXT1) assert isinstance(result, datatypes.number) assert result == datatypes.number(2)
def evaluate_as_number(self, context): return datatypes.number(self.evaluate(context))
def sin_function(context, number): """ The math:sin function returns the sine of the number. """ result = math.sin(number.evaluate_as_number(context)) return datatypes.number(result)
def random_function(context): """ The math:random function returns a random number from 0 to 1. """ return datatypes.number(random.random())