omlish 0.0.0.dev295__py3-none-any.whl → 0.0.0.dev297__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,184 @@
1
+ import io
2
+ import textwrap
3
+ import typing as ta
4
+
5
+ from .. import check
6
+ from .. import dataclasses as dc
7
+ from .. import dispatch
8
+ from .. import lang
9
+ from .content import Dom
10
+ from .content import iter_content
11
+
12
+
13
+ if ta.TYPE_CHECKING:
14
+ import markupsafe as ms
15
+
16
+ _HAS_MARKUPSAFE = True
17
+
18
+ else:
19
+ ms = lang.proxy_import('markupsafe')
20
+
21
+ _HAS_MARKUPSAFE = lang.can_import('markupsafe')
22
+
23
+
24
+ ##
25
+
26
+
27
+ @dc.dataclass()
28
+ class InvalidTagError(Exception):
29
+ pass
30
+
31
+
32
+ @dc.dataclass()
33
+ class StrForbiddenError(Exception):
34
+ pass
35
+
36
+
37
+ class Renderer:
38
+ def __init__(
39
+ self,
40
+ sb: io.StringIO | None,
41
+ *,
42
+ forbid_str: bool = False,
43
+ escape: ta.Callable[[str], str] | None = None,
44
+ indent: str | int | None = None,
45
+ indent_string_content: bool = False,
46
+ ) -> None:
47
+ super().__init__()
48
+
49
+ if sb is None:
50
+ sb = io.StringIO()
51
+ self._sb = sb
52
+
53
+ self._forbid_str = forbid_str
54
+ self._escape_fn = escape
55
+ if isinstance(indent, int):
56
+ indent = ' ' * indent
57
+ self._indent_unit: str | None = indent
58
+ self._indent_string_content = indent_string_content
59
+
60
+ self._level = 0
61
+ self._indent_cache: dict[int, str] = {}
62
+
63
+ @classmethod
64
+ def render_to_str(cls, o: ta.Any, **kwargs: ta.Any) -> str:
65
+ sb = io.StringIO()
66
+ cls(sb, **kwargs).render(o)
67
+ return sb.getvalue()
68
+
69
+ #
70
+
71
+ def _escape(self, s: str) -> str:
72
+ if _HAS_MARKUPSAFE and isinstance(s, ms.Markup):
73
+ return s
74
+ if type(s) is not str:
75
+ raise TypeError(s)
76
+ if (fn := self._escape_fn) is None:
77
+ return s
78
+ return fn(s)
79
+
80
+ def _check_tag(self, s: str) -> str:
81
+ if self._escape(s) != s:
82
+ raise InvalidTagError(s)
83
+ return s
84
+
85
+ #
86
+
87
+ def _indent_str(self) -> str | None:
88
+ if not (s := self._indent_unit):
89
+ return None
90
+ try:
91
+ return self._indent_cache[self._level]
92
+ except KeyError:
93
+ pass
94
+ ls = self._indent_cache[self._level] = s * self._level
95
+ return ls
96
+
97
+ def _write_indent(self) -> None:
98
+ if (s := self._indent_str()):
99
+ self._sb.write(s)
100
+
101
+ #
102
+
103
+ @dispatch.method
104
+ def render(self, o: ta.Any) -> None:
105
+ raise TypeError(o)
106
+
107
+ #
108
+
109
+ def _write_string_content(self, s: str) -> None:
110
+ if self._indent_string_content and (ls := self._indent_str()):
111
+ s = textwrap.indent(s, ls)
112
+ self._sb.write(s)
113
+
114
+ @render.register # noqa
115
+ def _render_str(self, s: str) -> None:
116
+ if self._forbid_str:
117
+ raise StrForbiddenError(s)
118
+ self._write_string_content(s)
119
+
120
+ @render.register # noqa
121
+ def _render_sequence(self, l: ta.Sequence) -> None:
122
+ for e in l:
123
+ self.render(e)
124
+
125
+ #
126
+
127
+ if _HAS_MARKUPSAFE:
128
+ @render.register
129
+ def _render_markup(self, m: ms.Markup) -> None:
130
+ self._write_string_content(m)
131
+
132
+ #
133
+
134
+ def _render_tag(self, tag: str) -> None:
135
+ self._sb.write(self._check_tag(tag))
136
+
137
+ def _render_attr_key(self, k: str) -> None:
138
+ self._sb.write(self._check_tag(k))
139
+
140
+ def _render_attr_value(self, v: ta.Any) -> None:
141
+ self._sb.write('"')
142
+ self._sb.write(check.isinstance(v, str)) # FIXME
143
+ self._sb.write('"')
144
+
145
+ @render.register # noqa
146
+ def _render_dom(self, n: Dom) -> None:
147
+ self._write_indent()
148
+
149
+ self._sb.write('<')
150
+ self._render_tag(n.tag)
151
+
152
+ for k, v in (n.attrs or {}).items():
153
+ self._sb.write(' ')
154
+ self._render_attr_key(k)
155
+ if v is not None:
156
+ self._sb.write('=')
157
+ self._render_attr_value(v)
158
+
159
+ i = -1
160
+ for i, c in enumerate(iter_content(n.body)):
161
+ if not i:
162
+ self._sb.write('>')
163
+
164
+ if self._indent_unit:
165
+ self._sb.write('\n')
166
+
167
+ self._level += 1
168
+ self.render(c)
169
+ self._level -= 1
170
+
171
+ if i >= 0:
172
+ if self._indent_unit:
173
+ self._sb.write('\n')
174
+ self._write_indent()
175
+
176
+ self._sb.write('</')
177
+ self._render_tag(n.tag)
178
+ self._sb.write('>')
179
+
180
+ else:
181
+ self._sb.write(' />')
182
+
183
+
184
+ render = Renderer.render_to_str
omlish/math/__init__.py CHANGED
@@ -0,0 +1,83 @@
1
+ from .bits import ( # noqa
2
+ get_bit,
3
+ get_bits,
4
+ set_bit,
5
+ set_bits,
6
+ )
7
+
8
+ from .c import ( # noqa
9
+ cdiv,
10
+ cmod,
11
+ )
12
+
13
+ from .fixed import ( # noqa
14
+ CheckedFixedWidthIntError,
15
+ OverflowFixedWidthIntError,
16
+ UnderflowFixedWidthIntError,
17
+
18
+ FixedWidthInt,
19
+
20
+ SignedInt,
21
+ UnsignedInt,
22
+
23
+ CheckedInt,
24
+ ClampedInt,
25
+
26
+ AnyInt8,
27
+ AnyInt16,
28
+ AnyInt32,
29
+ AnyInt64,
30
+ AnyInt128,
31
+
32
+ CheckedInt8,
33
+ CheckedInt16,
34
+ CheckedInt32,
35
+ CheckedInt64,
36
+ CheckedInt128,
37
+
38
+ CheckedUInt8,
39
+ CheckedUInt16,
40
+ CheckedUInt32,
41
+ CheckedUInt64,
42
+ CheckedUInt128,
43
+
44
+ ClampedInt8,
45
+ ClampedInt16,
46
+ ClampedInt32,
47
+ ClampedInt64,
48
+ ClampedInt128,
49
+
50
+ ClampedUInt8,
51
+ ClampedUInt16,
52
+ ClampedUInt32,
53
+ ClampedUInt64,
54
+ ClampedUInt128,
55
+
56
+ WrappedInt8,
57
+ WrappedInt16,
58
+ WrappedInt32,
59
+ WrappedInt64,
60
+ WrappedInt128,
61
+
62
+ WrappedUInt8,
63
+ WrappedUInt16,
64
+ WrappedUInt32,
65
+ WrappedUInt64,
66
+ WrappedUInt128,
67
+ )
68
+
69
+ from .floats import ( # noqa
70
+ isclose,
71
+ float_to_bytes,
72
+ bytes_to_float,
73
+ )
74
+
75
+ from .stats import ( # noqa
76
+ get_quantile,
77
+
78
+ Stats,
79
+ )
80
+
81
+ from .histogram import ( # noqa
82
+ SamplingHistogram,
83
+ )
omlish/math/bits.py CHANGED
@@ -1,10 +1,3 @@
1
- import functools
2
- import typing as ta
3
-
4
-
5
- ##
6
-
7
-
8
1
  def get_bit(bit: int, value: int) -> int:
9
2
  return (value >> bit) & 1
10
3
 
@@ -22,149 +15,3 @@ def set_bit(bit: int, bit_value: int, value: int) -> int:
22
15
 
23
16
  def set_bits(bits_from: int, num_bits: int, bits_value: int, value: int) -> int:
24
17
  return value & ~(((1 << num_bits) - 1) << bits_from) | (bits_value << bits_from)
25
-
26
-
27
- ##
28
-
29
-
30
- def _gen_scalar_proxy_method(name):
31
- def inner(self, *args, **kwargs):
32
- return self.__class__(orig(self, *args, **kwargs))
33
-
34
- orig = getattr(int, name)
35
- return functools.wraps(orig)(inner)
36
-
37
-
38
- def _gen_tuple_proxy_method(name):
39
- def inner(self, *args, **kwargs):
40
- return tuple(map(self.__class__, orig(self, *args, **kwargs)))
41
-
42
- orig = getattr(int, name)
43
- return functools.wraps(orig)(inner)
44
-
45
-
46
- class FixedWidthInt(int):
47
- BITS: ta.ClassVar[int]
48
- SIGNED: ta.ClassVar[bool]
49
-
50
- MIN: ta.ClassVar[int]
51
- MAX: ta.ClassVar[int]
52
-
53
- MASK: ta.ClassVar[int]
54
-
55
- def __init_subclass__(cls) -> None:
56
- super().__init_subclass__()
57
-
58
- if not isinstance(cls.BITS, int):
59
- raise TypeError(cls.BITS)
60
-
61
- if cls.SIGNED:
62
- cls.MIN = -(1 << (cls.BITS - 1))
63
- cls.MAX = (1 << (cls.BITS - 1)) - 1
64
- else:
65
- cls.MIN = 0
66
- cls.MAX = (1 << cls.BITS) - 1
67
-
68
- cls.MASK = (1 << cls.BITS) - 1
69
-
70
- @classmethod
71
- def clamp(cls, value: int) -> int:
72
- return ((value - cls.MIN) & cls.MASK) + cls.MIN
73
-
74
- def __new__(cls, value: int) -> 'FixedWidthInt':
75
- return super().__new__(cls, cls.clamp(value)) # noqa
76
-
77
- SCALAR_PROXY_METHODS = frozenset([
78
- '__abs__',
79
- '__add__',
80
- '__and__',
81
- '__floordiv__',
82
- '__invert__',
83
- '__lshift__',
84
- '__mod__',
85
- '__mul__',
86
- '__neg__',
87
- '__or__',
88
- '__pos__',
89
- '__pow__',
90
- '__radd__',
91
- '__rand__',
92
- '__rfloordiv__',
93
- '__rlshift__',
94
- '__rmod__',
95
- '__rmul__',
96
- '__ror__',
97
- '__rpow__',
98
- '__rrshift__',
99
- '__rshift__',
100
- '__rsub__',
101
- '__rtruediv__',
102
- '__rxor__',
103
- '__sub__',
104
- '__truediv__',
105
- '__xor__',
106
- ])
107
-
108
- TUPLE_PROXY_METHODS = frozenset([
109
- '__divmod__',
110
- '__rdivmod__',
111
- ])
112
-
113
- for _proxy_name in SCALAR_PROXY_METHODS:
114
- locals()[_proxy_name] = _gen_scalar_proxy_method(_proxy_name)
115
- for _proxy_name in TUPLE_PROXY_METHODS:
116
- locals()[_proxy_name] = _gen_tuple_proxy_method(_proxy_name)
117
- del _proxy_name
118
-
119
- def __repr__(self) -> str:
120
- return f'{self.__class__.__name__}({int(self)})'
121
-
122
-
123
- class Int8(FixedWidthInt):
124
- BITS = 8
125
- SIGNED = True
126
-
127
-
128
- class Int16(FixedWidthInt):
129
- BITS = 16
130
- SIGNED = True
131
-
132
-
133
- class Int32(FixedWidthInt):
134
- BITS = 32
135
- SIGNED = True
136
-
137
-
138
- class Int64(FixedWidthInt):
139
- BITS = 64
140
- SIGNED = True
141
-
142
-
143
- class Int128(FixedWidthInt):
144
- BITS = 128
145
- SIGNED = True
146
-
147
-
148
- class Uint8(FixedWidthInt):
149
- BITS = 8
150
- SIGNED = False
151
-
152
-
153
- class Uint16(FixedWidthInt):
154
- BITS = 16
155
- SIGNED = False
156
-
157
-
158
- class Uint32(FixedWidthInt):
159
- BITS = 32
160
- SIGNED = False
161
-
162
-
163
- class Uint64(FixedWidthInt):
164
- BITS = 64
165
- SIGNED = False
166
-
167
-
168
- class Uint128(FixedWidthInt):
169
- BITS = 128
170
- SIGNED = False
omlish/math/c.py ADDED
@@ -0,0 +1,13 @@
1
+ def cdiv(x: int, y: int) -> int:
2
+ """https://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html"""
3
+
4
+ if y == 0:
5
+ return 0
6
+ u = abs(x) // abs(y)
7
+ if (x < 0) ^ (y < 0):
8
+ u *= -1
9
+ return u
10
+
11
+
12
+ def cmod(x: int, y: int) -> int:
13
+ return x - (cdiv(x, y) * y)