1# Copyright 2008-2015 Nokia Networks 2# Copyright 2016- Robot Framework Foundation 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16from __future__ import division 17 18from operator import add, sub 19 20from .platform import PY2 21from .robottypes import is_integer 22from .unic import unic 23 24 25def roundup(number, ndigits=0, return_type=None): 26 """Rounds number to the given number of digits. 27 28 Numbers equally close to a certain precision are always rounded away from 29 zero. By default return value is float when ``ndigits`` is positive and 30 int otherwise, but that can be controlled with ``return_type``. 31 32 With the built-in ``round()`` rounding equally close numbers as well as 33 the return type depends on the Python version. 34 """ 35 result = _roundup(number, ndigits) 36 if not return_type: 37 return_type = float if ndigits > 0 else int 38 return return_type(result) 39 40 41# Python 2 rounds half away from zero (as taught in school) but Python 3 42# uses "bankers' rounding" that rounds half towards the even number. We want 43# consistent rounding and expect Python 2 style to be more familiar for users. 44if PY2: 45 _roundup = round 46else: 47 def _roundup(number, ndigits): 48 precision = 10 ** (-1 * ndigits) 49 if number % (0.5 * precision) == 0 and number % precision != 0: 50 operator = add if number > 0 else sub 51 number = operator(number, 0.1 * precision) 52 return round(number, ndigits) 53 54 55def printable_name(string, code_style=False): 56 """Generates and returns printable name from the given string. 57 58 Examples: 59 'simple' -> 'Simple' 60 'name with spaces' -> 'Name With Spaces' 61 'more spaces' -> 'More Spaces' 62 'Cases AND spaces' -> 'Cases AND Spaces' 63 '' -> '' 64 65 If 'code_style' is True: 66 67 'mixedCAPSCamel' -> 'Mixed CAPS Camel' 68 'camelCaseName' -> 'Camel Case Name' 69 'under_score_name' -> 'Under Score Name' 70 'under_and space' -> 'Under And Space' 71 'miXed_CAPS_nAMe' -> 'MiXed CAPS NAMe' 72 '' -> '' 73 """ 74 if code_style and '_' in string: 75 string = string.replace('_', ' ') 76 parts = string.split() 77 if code_style and len(parts) == 1 \ 78 and not (string.isalpha() and string.islower()): 79 parts = _split_camel_case(parts[0]) 80 return ' '.join(part[0].upper() + part[1:] for part in parts) 81 82 83def _split_camel_case(string): 84 tokens = [] 85 token = [] 86 for prev, char, next in zip(' ' + string, string, string[1:] + ' '): 87 if _is_camel_case_boundary(prev, char, next): 88 if token: 89 tokens.append(''.join(token)) 90 token = [char] 91 else: 92 token.append(char) 93 if token: 94 tokens.append(''.join(token)) 95 return tokens 96 97 98def _is_camel_case_boundary(prev, char, next): 99 if prev.isdigit(): 100 return not char.isdigit() 101 if char.isupper(): 102 return next.islower() or prev.isalpha() and not prev.isupper() 103 return char.isdigit() 104 105 106def plural_or_not(item): 107 count = item if is_integer(item) else len(item) 108 return '' if count == 1 else 's' 109 110 111def seq2str(sequence, quote="'", sep=', ', lastsep=' and '): 112 """Returns sequence in format `'item 1', 'item 2' and 'item 3'`.""" 113 sequence = [quote + unic(item) + quote for item in sequence] 114 if not sequence: 115 return '' 116 if len(sequence) == 1: 117 return sequence[0] 118 last_two = lastsep.join(sequence[-2:]) 119 return sep.join(sequence[:-2] + [last_two]) 120 121 122def seq2str2(sequence): 123 """Returns sequence in format `[ item 1 | item 2 | ... ]`.""" 124 if not sequence: 125 return '[ ]' 126 return '[ %s ]' % ' | '.join(unic(item) for item in sequence) 127