omlish 0.0.0.dev104__py3-none-any.whl → 0.0.0.dev106__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,6 +23,7 @@ A few notes on the implementation.
23
23
  import random
24
24
  import typing as ta
25
25
 
26
+ from ... import check
26
27
  from . import ast
27
28
  from . import exceptions
28
29
  from . import lexer
@@ -76,16 +77,18 @@ class Parser:
76
77
  _PROJECTION_STOP = 10
77
78
 
78
79
  # The _MAX_SIZE most recent expressions are cached in _CACHE dict.
79
- _CACHE: dict = {} # noqa
80
+ _CACHE: ta.ClassVar[dict[str, 'ParsedResult']] = {} # noqa
80
81
  _MAX_SIZE = 128
81
82
 
82
- def __init__(self, lookahead=2):
83
- self.tokenizer = None
84
- self._tokens = [None] * lookahead
83
+ def __init__(self, lookahead: int = 2) -> None:
84
+ super().__init__()
85
+
86
+ self._tokenizer: ta.Iterable[lexer.Token] | None = None
87
+ self._tokens: list[lexer.Token | None] = [None] * lookahead
85
88
  self._buffer_size = lookahead
86
89
  self._index = 0
87
90
 
88
- def parse(self, expression, options=None):
91
+ def parse(self, expression: str, options: visitor.Options | None = None) -> 'ParsedResult':
89
92
  cached = self._CACHE.get(expression)
90
93
  if cached is not None:
91
94
  return cached
@@ -98,7 +101,7 @@ class Parser:
98
101
 
99
102
  return parsed_result
100
103
 
101
- def _do_parse(self, expression, options=None):
104
+ def _do_parse(self, expression: str, options: visitor.Options | None = None) -> 'ParsedResult':
102
105
  try:
103
106
  return self._parse(expression, options)
104
107
 
@@ -114,15 +117,15 @@ class Parser:
114
117
  e.expression = expression
115
118
  raise
116
119
 
117
- def _parse(self, expression, options=None):
118
- self.tokenizer = lexer.Lexer().tokenize(expression, options)
119
- self._tokens = list(self.tokenizer)
120
+ def _parse(self, expression: str, options: visitor.Options | None = None) -> 'ParsedResult':
121
+ self._tokenizer = lexer.Lexer().tokenize(expression, options)
122
+ self._tokens = list(self._tokenizer)
120
123
  self._index = 0
121
124
 
122
125
  parsed = self._expression(binding_power=0)
123
126
 
124
127
  if self._current_token() != 'eof':
125
- t = self._lookahead_token(0)
128
+ t = check.not_none(self._lookahead_token(0))
126
129
  raise exceptions.ParseError(
127
130
  t['start'],
128
131
  t['value'],
@@ -132,8 +135,8 @@ class Parser:
132
135
 
133
136
  return ParsedResult(expression, parsed)
134
137
 
135
- def _expression(self, binding_power=0):
136
- left_token = self._lookahead_token(0)
138
+ def _expression(self, binding_power: int = 0) -> ast.Node:
139
+ left_token = check.not_none(self._lookahead_token(0))
137
140
 
138
141
  self._advance()
139
142
 
@@ -143,7 +146,7 @@ class Parser:
143
146
  self._error_nud_token,
144
147
  )
145
148
 
146
- left = nud_function(left_token)
149
+ left = nud_function(left_token) # noqa
147
150
 
148
151
  current_token = self._current_token()
149
152
  while binding_power < self.BINDING_POWER[current_token]:
@@ -153,7 +156,7 @@ class Parser:
153
156
  None,
154
157
  )
155
158
  if led is None:
156
- error_token = self._lookahead_token(0)
159
+ error_token = check.not_none(self._lookahead_token(0))
157
160
  self._error_led_token(error_token)
158
161
 
159
162
  else:
@@ -163,29 +166,29 @@ class Parser:
163
166
 
164
167
  return left
165
168
 
166
- def _token_nud_literal(self, token):
169
+ def _token_nud_literal(self, token: lexer.Token) -> ast.Node:
167
170
  return ast.literal(token['value'])
168
171
 
169
- def _token_nud_variable(self, token):
172
+ def _token_nud_variable(self, token: lexer.Token) -> ast.Node:
170
173
  return ast.variable_ref(token['value'][1:])
171
174
 
172
- def _token_nud_unquoted_identifier(self, token):
175
+ def _token_nud_unquoted_identifier(self, token: lexer.Token) -> ast.Node:
173
176
  if token['value'] == 'let' and self._current_token() == 'variable':
174
177
  return self._parse_let_expression()
175
178
  else:
176
179
  return ast.field(token['value'])
177
180
 
178
- def _parse_let_expression(self):
181
+ def _parse_let_expression(self) -> ast.Node:
179
182
  bindings = []
180
183
  while True:
181
- var_token = self._lookahead_token(0)
184
+ var_token = check.not_none(self._lookahead_token(0))
182
185
  # Strip off the '$'.
183
186
  varname = var_token['value'][1:]
184
187
  self._advance()
185
188
  self._match('assign')
186
189
  assign_expr = self._expression()
187
190
  bindings.append(ast.assign(varname, assign_expr))
188
- if self._is_in_keyword(self._lookahead_token(0)):
191
+ if self._is_in_keyword(check.not_none(self._lookahead_token(0))):
189
192
  self._advance()
190
193
  break
191
194
  else:
@@ -193,18 +196,18 @@ class Parser:
193
196
  expr = self._expression()
194
197
  return ast.let_expression(bindings, expr)
195
198
 
196
- def _is_in_keyword(self, token):
199
+ def _is_in_keyword(self, token: lexer.Token) -> bool:
197
200
  return (
198
201
  token['type'] == 'unquoted_identifier' and
199
202
  token['value'] == 'in'
200
203
  )
201
204
 
202
- def _token_nud_quoted_identifier(self, token):
205
+ def _token_nud_quoted_identifier(self, token: lexer.Token) -> ast.Node:
203
206
  field = ast.field(token['value'])
204
207
 
205
208
  # You can't have a quoted identifier as a function name.
206
209
  if self._current_token() == 'lparen':
207
- t = self._lookahead_token(0)
210
+ t = check.not_none(self._lookahead_token(0))
208
211
  raise exceptions.ParseError(
209
212
  0,
210
213
  t['value'],
@@ -214,7 +217,7 @@ class Parser:
214
217
 
215
218
  return field
216
219
 
217
- def _token_nud_star(self, token):
220
+ def _token_nud_star(self, token: lexer.Token) -> ast.Node:
218
221
  left = ast.identity()
219
222
  if self._current_token() == 'rbracket':
220
223
  right = ast.identity()
@@ -222,34 +225,34 @@ class Parser:
222
225
  right = self._parse_projection_rhs(self.BINDING_POWER['star'])
223
226
  return ast.value_projection(left, right)
224
227
 
225
- def _token_nud_filter(self, token):
228
+ def _token_nud_filter(self, token: lexer.Token) -> ast.Node:
226
229
  return self._token_led_filter(ast.identity())
227
230
 
228
- def _token_nud_lbrace(self, token):
231
+ def _token_nud_lbrace(self, token: lexer.Token) -> ast.Node:
229
232
  return self._parse_multi_select_hash()
230
233
 
231
- def _token_nud_lparen(self, token):
234
+ def _token_nud_lparen(self, token: lexer.Token) -> ast.Node:
232
235
  expression = self._expression()
233
236
  self._match('rparen')
234
237
  return expression
235
238
 
236
- def _token_nud_minus(self, token):
239
+ def _token_nud_minus(self, token: lexer.Token) -> ast.Node:
237
240
  return self._parse_arithmetic_unary(token)
238
241
 
239
- def _token_nud_plus(self, token):
242
+ def _token_nud_plus(self, token: lexer.Token) -> ast.Node:
240
243
  return self._parse_arithmetic_unary(token)
241
244
 
242
- def _token_nud_flatten(self, token):
245
+ def _token_nud_flatten(self, token: lexer.Token) -> ast.Node:
243
246
  left = ast.flatten(ast.identity())
244
247
  right = self._parse_projection_rhs(
245
248
  self.BINDING_POWER['flatten'])
246
249
  return ast.projection(left, right)
247
250
 
248
- def _token_nud_not(self, token):
251
+ def _token_nud_not(self, token: lexer.Token) -> ast.Node:
249
252
  expr = self._expression(self.BINDING_POWER['not'])
250
253
  return ast.not_expression(expr)
251
254
 
252
- def _token_nud_lbracket(self, token):
255
+ def _token_nud_lbracket(self, token: lexer.Token) -> ast.Node:
253
256
  if self._current_token() in ['number', 'colon']:
254
257
  right = self._parse_index_expression()
255
258
  # We could optimize this and remove the identity() node. We don't really need an index_expression node, we
@@ -265,7 +268,7 @@ class Parser:
265
268
  else:
266
269
  return self._parse_multi_select_list()
267
270
 
268
- def _parse_index_expression(self):
271
+ def _parse_index_expression(self) -> ast.Node:
269
272
  # We're here:
270
273
  # [<current>
271
274
  # ^
@@ -275,12 +278,12 @@ class Parser:
275
278
 
276
279
  else:
277
280
  # Parse the syntax [number]
278
- node = ast.index(self._lookahead_token(0)['value'])
281
+ node = ast.index(check.not_none(self._lookahead_token(0))['value'])
279
282
  self._advance()
280
283
  self._match('rbracket')
281
284
  return node
282
285
 
283
- def _parse_slice_expression(self):
286
+ def _parse_slice_expression(self) -> ast.Node:
284
287
  # [start:end:step]
285
288
  # Where start, end, and step are optional. The last colon is optional as well.
286
289
  parts = [None, None, None]
@@ -290,32 +293,32 @@ class Parser:
290
293
  if current_token == 'colon': # noqa
291
294
  index += 1
292
295
  if index == 3:
293
- self._raise_parse_error_for_token(self._lookahead_token(0), 'syntax error')
296
+ self._raise_parse_error_for_token(check.not_none(self._lookahead_token(0)), 'syntax error')
294
297
  self._advance()
295
298
 
296
299
  elif current_token == 'number': # noqa
297
- parts[index] = self._lookahead_token(0)['value']
300
+ parts[index] = check.not_none(self._lookahead_token(0))['value']
298
301
  self._advance()
299
302
 
300
303
  else:
301
- self._raise_parse_error_for_token(self._lookahead_token(0), 'syntax error')
304
+ self._raise_parse_error_for_token(check.not_none(self._lookahead_token(0)), 'syntax error')
302
305
 
303
306
  current_token = self._current_token()
304
307
 
305
308
  self._match('rbracket')
306
309
  return ast.slice(*parts)
307
310
 
308
- def _token_nud_current(self, token):
311
+ def _token_nud_current(self, token: lexer.Token) -> ast.Node:
309
312
  return ast.current_node()
310
313
 
311
- def _token_nud_root(self, token):
314
+ def _token_nud_root(self, token: lexer.Token) -> ast.Node:
312
315
  return ast.root_node()
313
316
 
314
- def _token_nud_expref(self, token):
317
+ def _token_nud_expref(self, token: lexer.Token) -> ast.Node:
315
318
  expression = self._expression(self.BINDING_POWER['expref'])
316
319
  return ast.expref(expression)
317
320
 
318
- def _token_led_dot(self, left):
321
+ def _token_led_dot(self, left: ast.Node) -> ast.Node:
319
322
  if self._current_token() != 'star':
320
323
  right = self._parse_dot_rhs(self.BINDING_POWER['dot'])
321
324
  if left['type'] == 'subexpression':
@@ -331,24 +334,24 @@ class Parser:
331
334
  right = self._parse_projection_rhs(self.BINDING_POWER['dot'])
332
335
  return ast.value_projection(left, right)
333
336
 
334
- def _token_led_pipe(self, left):
337
+ def _token_led_pipe(self, left: ast.Node) -> ast.Node:
335
338
  right = self._expression(self.BINDING_POWER['pipe'])
336
339
  return ast.pipe(left, right)
337
340
 
338
- def _token_led_or(self, left):
341
+ def _token_led_or(self, left: ast.Node) -> ast.Node:
339
342
  right = self._expression(self.BINDING_POWER['or'])
340
343
  return ast.or_expression(left, right)
341
344
 
342
- def _token_led_and(self, left):
345
+ def _token_led_and(self, left: ast.Node) -> ast.Node:
343
346
  right = self._expression(self.BINDING_POWER['and'])
344
347
  return ast.and_expression(left, right)
345
348
 
346
- def _token_led_lparen(self, left):
349
+ def _token_led_lparen(self, left: ast.Node) -> ast.Node:
347
350
  if left['type'] != 'field':
348
351
  # 0 - first func arg or closing paren.
349
352
  # -1 - '(' token
350
353
  # -2 - invalid function "name".
351
- prev_t = self._lookahead_token(-2)
354
+ prev_t = check.not_none(self._lookahead_token(-2))
352
355
  raise exceptions.ParseError(
353
356
  prev_t['start'],
354
357
  prev_t['value'],
@@ -368,7 +371,7 @@ class Parser:
368
371
  function_node = ast.function_expression(name, args)
369
372
  return function_node
370
373
 
371
- def _token_led_filter(self, left):
374
+ def _token_led_filter(self, left: ast.Node) -> ast.Node:
372
375
  # Filters are projections.
373
376
  condition = self._expression(0)
374
377
  self._match('rbracket')
@@ -378,52 +381,52 @@ class Parser:
378
381
  right = self._parse_projection_rhs(self.BINDING_POWER['filter'])
379
382
  return ast.filter_projection(left, right, condition)
380
383
 
381
- def _token_led_eq(self, left):
384
+ def _token_led_eq(self, left: ast.Node) -> ast.Node:
382
385
  return self._parse_comparator(left, 'eq')
383
386
 
384
- def _token_led_ne(self, left):
387
+ def _token_led_ne(self, left: ast.Node) -> ast.Node:
385
388
  return self._parse_comparator(left, 'ne')
386
389
 
387
- def _token_led_gt(self, left):
390
+ def _token_led_gt(self, left: ast.Node) -> ast.Node:
388
391
  return self._parse_comparator(left, 'gt')
389
392
 
390
- def _token_led_gte(self, left):
393
+ def _token_led_gte(self, left: ast.Node) -> ast.Node:
391
394
  return self._parse_comparator(left, 'gte')
392
395
 
393
- def _token_led_lt(self, left):
396
+ def _token_led_lt(self, left: ast.Node) -> ast.Node:
394
397
  return self._parse_comparator(left, 'lt')
395
398
 
396
- def _token_led_lte(self, left):
399
+ def _token_led_lte(self, left: ast.Node) -> ast.Node:
397
400
  return self._parse_comparator(left, 'lte')
398
401
 
399
- def _token_led_div(self, left):
402
+ def _token_led_div(self, left: ast.Node) -> ast.Node:
400
403
  return self._parse_arithmetic(left, 'div')
401
404
 
402
- def _token_led_divide(self, left):
405
+ def _token_led_divide(self, left: ast.Node) -> ast.Node:
403
406
  return self._parse_arithmetic(left, 'divide')
404
407
 
405
- def _token_led_minus(self, left):
408
+ def _token_led_minus(self, left: ast.Node) -> ast.Node:
406
409
  return self._parse_arithmetic(left, 'minus')
407
410
 
408
- def _token_led_modulo(self, left):
411
+ def _token_led_modulo(self, left: ast.Node) -> ast.Node:
409
412
  return self._parse_arithmetic(left, 'modulo')
410
413
 
411
- def _token_led_multiply(self, left):
414
+ def _token_led_multiply(self, left: ast.Node) -> ast.Node:
412
415
  return self._parse_arithmetic(left, 'multiply')
413
416
 
414
- def _token_led_plus(self, left):
417
+ def _token_led_plus(self, left: ast.Node) -> ast.Node:
415
418
  return self._parse_arithmetic(left, 'plus')
416
419
 
417
- def _token_led_star(self, left):
420
+ def _token_led_star(self, left: ast.Node) -> ast.Node:
418
421
  return self._parse_arithmetic(left, 'multiply')
419
422
 
420
- def _token_led_flatten(self, left):
423
+ def _token_led_flatten(self, left: ast.Node) -> ast.Node:
421
424
  left = ast.flatten(left)
422
425
  right = self._parse_projection_rhs(self.BINDING_POWER['flatten'])
423
426
  return ast.projection(left, right)
424
427
 
425
- def _token_led_lbracket(self, left):
426
- token = self._lookahead_token(0)
428
+ def _token_led_lbracket(self, left: ast.Node) -> ast.Node:
429
+ token = check.not_none(self._lookahead_token(0))
427
430
  if token['type'] in ['number', 'colon']:
428
431
  right = self._parse_index_expression()
429
432
  if left['type'] == 'index_expression':
@@ -442,7 +445,7 @@ class Parser:
442
445
  right = self._parse_projection_rhs(self.BINDING_POWER['star'])
443
446
  return ast.projection(left, right)
444
447
 
445
- def _project_if_slice(self, left, right):
448
+ def _project_if_slice(self, left: ast.Node, right: ast.Node) -> ast.Node:
446
449
  index_expr = ast.index_expression([left, right])
447
450
  if right['type'] == 'slice':
448
451
  return ast.projection(
@@ -452,20 +455,20 @@ class Parser:
452
455
  else:
453
456
  return index_expr
454
457
 
455
- def _parse_comparator(self, left, comparator):
458
+ def _parse_comparator(self, left: ast.Node, comparator: str) -> ast.Node:
456
459
  right = self._expression(self.BINDING_POWER[comparator])
457
460
  return ast.comparator(comparator, left, right)
458
461
 
459
- def _parse_arithmetic_unary(self, token):
462
+ def _parse_arithmetic_unary(self, token: lexer.Token) -> ast.Node:
460
463
  expression = self._expression(self.BINDING_POWER[token['type']])
461
464
  return ast.arithmetic_unary(token['type'], expression)
462
465
 
463
- def _parse_arithmetic(self, left, operator):
466
+ def _parse_arithmetic(self, left: ast.Node, operator: str) -> ast.Node:
464
467
  right = self._expression(self.BINDING_POWER[operator])
465
468
  return ast.arithmetic(operator, left, right)
466
469
 
467
- def _parse_multi_select_list(self):
468
- expressions = []
470
+ def _parse_multi_select_list(self) -> ast.Node:
471
+ expressions: list[ast.Node] = []
469
472
  while True:
470
473
  expression = self._expression()
471
474
  expressions.append(expression)
@@ -476,10 +479,10 @@ class Parser:
476
479
  self._match('rbracket')
477
480
  return ast.multi_select_list(expressions)
478
481
 
479
- def _parse_multi_select_hash(self):
482
+ def _parse_multi_select_hash(self) -> ast.Node:
480
483
  pairs = []
481
484
  while True:
482
- key_token = self._lookahead_token(0)
485
+ key_token = check.not_none(self._lookahead_token(0))
483
486
 
484
487
  # Before getting the token value, verify it's an identifier.
485
488
  self._match_multiple_tokens(token_types=['quoted_identifier', 'unquoted_identifier'])
@@ -500,7 +503,7 @@ class Parser:
500
503
 
501
504
  return ast.multi_select_dict(nodes=pairs)
502
505
 
503
- def _parse_projection_rhs(self, binding_power):
506
+ def _parse_projection_rhs(self, binding_power: int) -> ast.Node:
504
507
  # Parse the right hand side of the projection.
505
508
  if self.BINDING_POWER[self._current_token()] < self._PROJECTION_STOP:
506
509
  # BP of 10 are all the tokens that stop a projection.
@@ -517,11 +520,11 @@ class Parser:
517
520
  right = self._parse_dot_rhs(binding_power)
518
521
 
519
522
  else:
520
- self._raise_parse_error_for_token(self._lookahead_token(0), 'syntax error')
523
+ self._raise_parse_error_for_token(check.not_none(self._lookahead_token(0)), 'syntax error')
521
524
 
522
525
  return right
523
526
 
524
- def _parse_dot_rhs(self, binding_power):
527
+ def _parse_dot_rhs(self, binding_power: int) -> ast.Node:
525
528
  # From the grammar:
526
529
  # expression '.' ( identifier /
527
530
  # multi-select-list /
@@ -544,13 +547,13 @@ class Parser:
544
547
  return self._parse_multi_select_hash()
545
548
 
546
549
  else:
547
- t = self._lookahead_token(0)
550
+ t = check.not_none(self._lookahead_token(0))
548
551
  allowed = ['quoted_identifier', 'unquoted_identifier', 'lbracket', 'lbrace']
549
552
  msg = f'Expecting: {allowed}, got: {t["type"]}'
550
553
  self._raise_parse_error_for_token(t, msg)
551
554
  raise RuntimeError # noqa
552
555
 
553
- def _error_nud_token(self, token):
556
+ def _error_nud_token(self, token: lexer.Token) -> ta.NoReturn:
554
557
  if token['type'] == 'eof':
555
558
  raise exceptions.IncompleteExpressionError(
556
559
  token['start'],
@@ -560,10 +563,10 @@ class Parser:
560
563
 
561
564
  self._raise_parse_error_for_token(token, 'invalid token')
562
565
 
563
- def _error_led_token(self, token):
566
+ def _error_led_token(self, token: lexer.Token) -> ta.NoReturn:
564
567
  self._raise_parse_error_for_token(token, 'invalid token')
565
568
 
566
- def _match(self, token_type=None):
569
+ def _match(self, token_type: str | None = None) -> None:
567
570
  # inline'd self._current_token()
568
571
  if self._current_token() == token_type:
569
572
  # inline'd self._advance()
@@ -571,24 +574,24 @@ class Parser:
571
574
  else:
572
575
  self._raise_parse_error_maybe_eof(token_type, self._lookahead_token(0))
573
576
 
574
- def _match_multiple_tokens(self, token_types):
577
+ def _match_multiple_tokens(self, token_types: ta.Container[str]) -> None:
575
578
  if self._current_token() not in token_types:
576
579
  self._raise_parse_error_maybe_eof(token_types, self._lookahead_token(0))
577
580
  self._advance()
578
581
 
579
- def _advance(self):
582
+ def _advance(self) -> None:
580
583
  self._index += 1
581
584
 
582
- def _current_token(self):
583
- return self._tokens[self._index]['type'] # type: ignore
585
+ def _current_token(self) -> str:
586
+ return check.not_none(self._tokens[self._index])['type']
584
587
 
585
- def _lookahead(self, number):
586
- return self._tokens[self._index + number]['type'] # noqa
588
+ def _lookahead(self, number: int) -> str:
589
+ return check.not_none(self._tokens[self._index + number])['type']
587
590
 
588
- def _lookahead_token(self, number):
591
+ def _lookahead_token(self, number: int) -> lexer.Token | None:
589
592
  return self._tokens[self._index + number]
590
593
 
591
- def _raise_parse_error_for_token(self, token, reason) -> ta.NoReturn:
594
+ def _raise_parse_error_for_token(self, token: lexer.Token, reason: str) -> ta.NoReturn:
592
595
  lex_position = token['start']
593
596
  actual_value = token['value']
594
597
  actual_type = token['type']
@@ -631,15 +634,17 @@ class Parser:
631
634
 
632
635
 
633
636
  class ParsedResult:
634
- def __init__(self, expression, parsed):
637
+ def __init__(self, expression: str, parsed: ast.Node) -> None:
638
+ super().__init__()
639
+
635
640
  self.expression = expression
636
641
  self.parsed = parsed
637
642
 
638
- def search(self, value, options=None):
643
+ def search(self, value: ta.Any, options: visitor.Options | None = None) -> ta.Any:
639
644
  evaluator = visitor.TreeInterpreter(options)
640
645
  return evaluator.evaluate(self.parsed, value)
641
646
 
642
- def _render_dot_file(self):
647
+ def _render_dot_file(self) -> str:
643
648
  """
644
649
  Render the parsed AST as a dot file.
645
650
 
@@ -652,13 +657,13 @@ class ParsedResult:
652
657
  contents = renderer.visit(self.parsed)
653
658
  return contents
654
659
 
655
- def __repr__(self):
660
+ def __repr__(self) -> str:
656
661
  return repr(self.parsed)
657
662
 
658
663
 
659
- def compile(expression, options=None): # noqa
664
+ def compile(expression: str, options: visitor.Options | None = None) -> ParsedResult: # noqa
660
665
  return Parser().parse(expression, options=options)
661
666
 
662
667
 
663
- def search(expression, data, options=None):
668
+ def search(expression: str, data: ta.Any, options: visitor.Options | None = None) -> ta.Any:
664
669
  return compile(expression, options).search(data, options=options)
@@ -9,6 +9,8 @@ class ScopedChainDict:
9
9
  """
10
10
 
11
11
  def __init__(self, *scopes):
12
+ super().__init__()
13
+
12
14
  # The scopes are evaluated starting at the top of the stack (the most recently pushed scope via .push_scope()).
13
15
  # If we use a normal list() and push/pop scopes by adding/removing to the end of the list, we'd have to always
14
16
  # call reversed(self._scopes) whenever we resolve a key, because the end of the list is the top of the stack.
@@ -4,17 +4,18 @@ import typing as ta
4
4
 
5
5
  from . import exceptions
6
6
  from . import functions
7
+ from .ast import Node
7
8
  from .scope import ScopedChainDict
8
9
 
9
10
 
10
- def _equals(x, y):
11
+ def _equals(x: ta.Any, y: ta.Any) -> bool:
11
12
  if _is_special_number_case(x, y):
12
13
  return False
13
14
  else:
14
15
  return x == y
15
16
 
16
17
 
17
- def _is_special_number_case(x, y):
18
+ def _is_special_number_case(x: ta.Any, y: ta.Any) -> bool | None:
18
19
  # We need to special case comparing 0 or 1 to True/False. While normally comparing any integer other than 0/1 to
19
20
  # True/False will always return False. However 0/1 have this:
20
21
  # >>> 0 == True
@@ -39,13 +40,13 @@ def _is_special_number_case(x, y):
39
40
  return None
40
41
 
41
42
 
42
- def _is_comparable(x):
43
+ def _is_comparable(x: ta.Any) -> bool:
43
44
  # The spec doesn't officially support string types yet, but enough people are relying on this behavior that it's
44
45
  # been added back. This should eventually become part of the official spec.
45
46
  return _is_actual_number(x) or isinstance(x, str)
46
47
 
47
48
 
48
- def _is_actual_number(x):
49
+ def _is_actual_number(x: ta.Any) -> bool:
49
50
  # We need to handle python's quirkiness with booleans, specifically:
50
51
  #
51
52
  # >>> isinstance(False, int)
@@ -62,10 +63,12 @@ class Options:
62
63
 
63
64
  def __init__(
64
65
  self,
65
- dict_cls=None,
66
+ dict_cls: type | None = None,
66
67
  custom_functions=None,
67
- enable_legacy_literals=False,
68
- ):
68
+ enable_legacy_literals: bool = False,
69
+ ) -> None:
70
+ super().__init__()
71
+
69
72
  #: The class to use when creating a dict. The interpreter may create dictionaries during the evaluation of a
70
73
  # Jmespath expression. For example, a multi-select hash will create a dictionary. By default we use a dict()
71
74
  # type. You can set this value to change what dict type is used. The most common reason you would change this
@@ -82,6 +85,7 @@ class Options:
82
85
 
83
86
  class _Expression:
84
87
  def __init__(self, expression, interpreter):
88
+ super().__init__()
85
89
  self.expression = expression
86
90
  self.interpreter = interpreter
87
91
 
@@ -91,6 +95,7 @@ class _Expression:
91
95
 
92
96
  class Visitor:
93
97
  def __init__(self):
98
+ super().__init__()
94
99
  self._method_cache = {}
95
100
 
96
101
  def visit(self, node, *args, **kwargs):
@@ -156,7 +161,7 @@ class TreeInterpreter(Visitor):
156
161
  def default_visit(self, node, *args, **kwargs):
157
162
  raise NotImplementedError(node['type'])
158
163
 
159
- def evaluate(self, ast, root):
164
+ def evaluate(self, ast, root: Node) -> ta.Any:
160
165
  self._root = root
161
166
  return self.visit(ast, root)
162
167
 
omlish/text/random.py ADDED
@@ -0,0 +1,7 @@
1
+ import random
2
+ import string
3
+
4
+
5
+ def generate_random_text(size: int) -> str:
6
+ chars = string.ascii_letters + string.digits + string.punctuation
7
+ return ''.join(random.choices(chars, k=size))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev104
3
+ Version: 0.0.0.dev106
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause