288 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			288 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								import collections
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_named_tuple(name, dict):
							 | 
						||
| 
								 | 
							
								    return collections.namedtuple(name, dict.keys())(*dict.values())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Align = get_named_tuple(
							 | 
						||
| 
								 | 
							
								    "align",
							 | 
						||
| 
								 | 
							
								    {"default": "", "left": "<", "right": ">", "center": "^", "right_sign": "="},
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Group = get_named_tuple("group", {"no": "", "yes": ","})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Padding = get_named_tuple("padding", {"no": "", "yes": "0"})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Prefix = get_named_tuple(
							 | 
						||
| 
								 | 
							
								    "prefix",
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        "yocto": 10**-24,
							 | 
						||
| 
								 | 
							
								        "zepto": 10**-21,
							 | 
						||
| 
								 | 
							
								        "atto": 10**-18,
							 | 
						||
| 
								 | 
							
								        "femto": 10**-15,
							 | 
						||
| 
								 | 
							
								        "pico": 10**-12,
							 | 
						||
| 
								 | 
							
								        "nano": 10**-9,
							 | 
						||
| 
								 | 
							
								        "micro": 10**-6,
							 | 
						||
| 
								 | 
							
								        "milli": 10**-3,
							 | 
						||
| 
								 | 
							
								        "none": None,
							 | 
						||
| 
								 | 
							
								        "kilo": 10**3,
							 | 
						||
| 
								 | 
							
								        "mega": 10**6,
							 | 
						||
| 
								 | 
							
								        "giga": 10**9,
							 | 
						||
| 
								 | 
							
								        "tera": 10**12,
							 | 
						||
| 
								 | 
							
								        "peta": 10**15,
							 | 
						||
| 
								 | 
							
								        "exa": 10**18,
							 | 
						||
| 
								 | 
							
								        "zetta": 10**21,
							 | 
						||
| 
								 | 
							
								        "yotta": 10**24,
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Scheme = get_named_tuple(
							 | 
						||
| 
								 | 
							
								    "scheme",
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        "default": "",
							 | 
						||
| 
								 | 
							
								        "decimal": "r",
							 | 
						||
| 
								 | 
							
								        "decimal_integer": "d",
							 | 
						||
| 
								 | 
							
								        "decimal_or_exponent": "g",
							 | 
						||
| 
								 | 
							
								        "decimal_si_prefix": "s",
							 | 
						||
| 
								 | 
							
								        "exponent": "e",
							 | 
						||
| 
								 | 
							
								        "fixed": "f",
							 | 
						||
| 
								 | 
							
								        "percentage": "%",
							 | 
						||
| 
								 | 
							
								        "percentage_rounded": "p",
							 | 
						||
| 
								 | 
							
								        "binary": "b",
							 | 
						||
| 
								 | 
							
								        "octal": "o",
							 | 
						||
| 
								 | 
							
								        "lower_case_hex": "x",
							 | 
						||
| 
								 | 
							
								        "upper_case_hex": "X",
							 | 
						||
| 
								 | 
							
								        "unicode": "c",
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Sign = get_named_tuple(
							 | 
						||
| 
								 | 
							
								    "sign",
							 | 
						||
| 
								 | 
							
								    {"default": "", "negative": "-", "positive": "+", "parantheses": "(", "space": " "},
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Symbol = get_named_tuple(
							 | 
						||
| 
								 | 
							
								    "symbol", {"no": "", "yes": "$", "binary": "#b", "octal": "#o", "hex": "#x"}
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Trim = get_named_tuple("trim", {"no": "", "yes": "~"})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Format:
							 | 
						||
| 
								 | 
							
								    def __init__(self, **kwargs):
							 | 
						||
| 
								 | 
							
								        self._locale = {}
							 | 
						||
| 
								 | 
							
								        self._nully = ""
							 | 
						||
| 
								 | 
							
								        self._prefix = Prefix.none
							 | 
						||
| 
								 | 
							
								        self._specifier = {
							 | 
						||
| 
								 | 
							
								            "align": Align.default,
							 | 
						||
| 
								 | 
							
								            "fill": "",
							 | 
						||
| 
								 | 
							
								            "group": Group.no,
							 | 
						||
| 
								 | 
							
								            "width": "",
							 | 
						||
| 
								 | 
							
								            "padding": Padding.no,
							 | 
						||
| 
								 | 
							
								            "precision": "",
							 | 
						||
| 
								 | 
							
								            "sign": Sign.default,
							 | 
						||
| 
								 | 
							
								            "symbol": Symbol.no,
							 | 
						||
| 
								 | 
							
								            "trim": Trim.no,
							 | 
						||
| 
								 | 
							
								            "type": Scheme.default,
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        valid_methods = [
							 | 
						||
| 
								 | 
							
								            m for m in dir(self.__class__) if m[0] != "_" and m != "to_plotly_json"
							 | 
						||
| 
								 | 
							
								        ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for kw, val in kwargs.items():
							 | 
						||
| 
								 | 
							
								            if kw not in valid_methods:
							 | 
						||
| 
								 | 
							
								                raise TypeError(
							 | 
						||
| 
								 | 
							
								                    "{0} is not a format method. Expected one of".format(kw),
							 | 
						||
| 
								 | 
							
								                    str(list(valid_methods)),
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            getattr(self, kw)(val)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _validate_char(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_string(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if len(value) != 1:
							 | 
						||
| 
								 | 
							
								            raise ValueError("expected value to a string of length one")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _validate_non_negative_integer_or_none(self, value):
							 | 
						||
| 
								 | 
							
								        if value is None:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not isinstance(value, int):
							 | 
						||
| 
								 | 
							
								            raise TypeError("expected value to be an integer")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if value < 0:
							 | 
						||
| 
								 | 
							
								            raise ValueError("expected value to be non-negative", str(value))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _validate_named(self, value, named_values):
							 | 
						||
| 
								 | 
							
								        if value not in named_values:
							 | 
						||
| 
								 | 
							
								            raise TypeError("expected value to be one of", str(list(named_values)))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _validate_string(self, value):
							 | 
						||
| 
								 | 
							
								        if not isinstance(value, (str, "".__class__)):
							 | 
						||
| 
								 | 
							
								            raise TypeError("expected value to be a string")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Specifier
							 | 
						||
| 
								 | 
							
								    def align(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_named(value, Align)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._specifier["align"] = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def fill(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_char(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._specifier["fill"] = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def group(self, value):
							 | 
						||
| 
								 | 
							
								        if isinstance(value, bool):
							 | 
						||
| 
								 | 
							
								            value = Group.yes if value else Group.no
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._validate_named(value, Group)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._specifier["group"] = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def padding(self, value):
							 | 
						||
| 
								 | 
							
								        if isinstance(value, bool):
							 | 
						||
| 
								 | 
							
								            value = Padding.yes if value else Padding.no
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._validate_named(value, Padding)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._specifier["padding"] = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def padding_width(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_non_negative_integer_or_none(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._specifier["width"] = value if value is not None else ""
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def precision(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_non_negative_integer_or_none(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._specifier["precision"] = ".{0}".format(value) if value is not None else ""
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def scheme(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_named(value, Scheme)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._specifier["type"] = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def sign(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_named(value, Sign)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._specifier["sign"] = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def symbol(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_named(value, Symbol)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._specifier["symbol"] = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def trim(self, value):
							 | 
						||
| 
								 | 
							
								        if isinstance(value, bool):
							 | 
						||
| 
								 | 
							
								            value = Trim.yes if value else Trim.no
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._validate_named(value, Trim)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._specifier["trim"] = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Locale
							 | 
						||
| 
								 | 
							
								    def symbol_prefix(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_string(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if "symbol" not in self._locale:
							 | 
						||
| 
								 | 
							
								            self._locale["symbol"] = [value, ""]
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self._locale["symbol"][0] = value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def symbol_suffix(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_string(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if "symbol" not in self._locale:
							 | 
						||
| 
								 | 
							
								            self._locale["symbol"] = ["", value]
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self._locale["symbol"][1] = value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def decimal_delimiter(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_char(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._locale["decimal"] = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def group_delimiter(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_char(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._locale["group"] = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def groups(self, groups):
							 | 
						||
| 
								 | 
							
								        groups = (
							 | 
						||
| 
								 | 
							
								            groups
							 | 
						||
| 
								 | 
							
								            if isinstance(groups, list)
							 | 
						||
| 
								 | 
							
								            else [groups]
							 | 
						||
| 
								 | 
							
								            if isinstance(groups, int)
							 | 
						||
| 
								 | 
							
								            else None
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not isinstance(groups, list):
							 | 
						||
| 
								 | 
							
								            raise TypeError("expected groups to be an integer or a list of integers")
							 | 
						||
| 
								 | 
							
								        if len(groups) == 0:
							 | 
						||
| 
								 | 
							
								            raise ValueError(
							 | 
						||
| 
								 | 
							
								                "expected groups to be an integer or a list of " "one or more integers"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for group in groups:
							 | 
						||
| 
								 | 
							
								            if not isinstance(group, int):
							 | 
						||
| 
								 | 
							
								                raise TypeError("expected entry to be an integer")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if group <= 0:
							 | 
						||
| 
								 | 
							
								                raise ValueError("expected entry to be a non-negative integer")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._locale["grouping"] = groups
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Nully
							 | 
						||
| 
								 | 
							
								    def nully(self, value):
							 | 
						||
| 
								 | 
							
								        self._nully = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Prefix
							 | 
						||
| 
								 | 
							
								    def si_prefix(self, value):
							 | 
						||
| 
								 | 
							
								        self._validate_named(value, Prefix)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._prefix = value
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def to_plotly_json(self):
							 | 
						||
| 
								 | 
							
								        f = {}
							 | 
						||
| 
								 | 
							
								        f["locale"] = self._locale.copy()
							 | 
						||
| 
								 | 
							
								        f["nully"] = self._nully
							 | 
						||
| 
								 | 
							
								        f["prefix"] = self._prefix
							 | 
						||
| 
								 | 
							
								        aligned = self._specifier["align"] != Align.default
							 | 
						||
| 
								 | 
							
								        f["specifier"] = "{}{}{}{}{}{}{}{}{}{}".format(
							 | 
						||
| 
								 | 
							
								            self._specifier["fill"] if aligned else "",
							 | 
						||
| 
								 | 
							
								            self._specifier["align"],
							 | 
						||
| 
								 | 
							
								            self._specifier["sign"],
							 | 
						||
| 
								 | 
							
								            self._specifier["symbol"],
							 | 
						||
| 
								 | 
							
								            self._specifier["padding"],
							 | 
						||
| 
								 | 
							
								            self._specifier["width"],
							 | 
						||
| 
								 | 
							
								            self._specifier["group"],
							 | 
						||
| 
								 | 
							
								            self._specifier["precision"],
							 | 
						||
| 
								 | 
							
								            self._specifier["trim"],
							 | 
						||
| 
								 | 
							
								            self._specifier["type"],
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return f
							 |