omdev 0.0.0.dev441__py3-none-any.whl → 0.0.0.dev443__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.
omdev/interp/venvs.py CHANGED
@@ -21,6 +21,7 @@ from .types import InterpSpecifier
21
21
  class InterpVenvConfig:
22
22
  interp: ta.Optional[str] = None
23
23
  requires: ta.Optional[ta.Sequence[str]] = None
24
+ requires_pats: ta.Optional[ta.Sequence[str]] = None
24
25
  use_uv: ta.Optional[bool] = None
25
26
 
26
27
 
@@ -33,6 +33,12 @@ from omlish.lite.check import check
33
33
  from .specifiers import Specifier
34
34
 
35
35
 
36
+ RequiresMarkerVar = ta.Union['RequiresVariable', 'RequiresValue'] # ta.TypeAlias
37
+
38
+ RequiresMarkerAtom = ta.Union['RequiresMarkerItem', ta.Sequence['RequiresMarkerAtom']] # ta.TypeAlias
39
+ RequiresMarkerList = ta.Sequence[ta.Union['RequiresMarkerList', 'RequiresMarkerAtom', str]] # ta.TypeAlias
40
+
41
+
36
42
  ##
37
43
 
38
44
 
@@ -229,12 +235,6 @@ class RequiresOp(RequiresNode):
229
235
  return str(self)
230
236
 
231
237
 
232
- RequiresMarkerVar = ta.Union['RequiresVariable', 'RequiresValue']
233
-
234
- RequiresMarkerAtom = ta.Union['RequiresMarkerItem', ta.Sequence['RequiresMarkerAtom']]
235
- RequiresMarkerList = ta.Sequence[ta.Union['RequiresMarkerList', 'RequiresMarkerAtom', str]]
236
-
237
-
238
238
  class RequiresMarkerItem(ta.NamedTuple):
239
239
  l: ta.Union[RequiresVariable, RequiresValue]
240
240
  op: RequiresOp
omdev/pyproject/reqs.py CHANGED
@@ -4,12 +4,16 @@ TODO:
4
4
  """
5
5
  # ruff: noqa: UP007 UP045
6
6
  import os.path
7
+ import re
7
8
  import tempfile
8
9
  import typing as ta
9
10
 
10
11
  from omlish.lite.cached import cached_nullary
11
12
  from omlish.logs.modules import get_module_logger
12
13
 
14
+ from ..packaging.requires import RequiresParserSyntaxError
15
+ from ..packaging.requires import parse_requirement
16
+
13
17
 
14
18
  log = get_module_logger(globals()) # noqa
15
19
 
@@ -20,11 +24,14 @@ log = get_module_logger(globals()) # noqa
20
24
  class RequirementsRewriter:
21
25
  def __init__(
22
26
  self,
27
+ *,
23
28
  venv: ta.Optional[str] = None,
29
+ only_pats: ta.Optional[ta.Sequence[re.Pattern]] = None,
24
30
  ) -> None:
25
31
  super().__init__()
26
32
 
27
33
  self._venv = venv
34
+ self._only_pats = only_pats
28
35
 
29
36
  @cached_nullary
30
37
  def _tmp_dir(self) -> str:
@@ -40,17 +47,32 @@ class RequirementsRewriter:
40
47
  out_lines = []
41
48
 
42
49
  for l in in_lines:
43
- if self.VENV_MAGIC in l:
44
- lp, _, rp = l.partition(self.VENV_MAGIC)
45
- rp = rp.partition('#')[0]
50
+ if l.split('#')[0].strip():
46
51
  omit = False
47
- for v in rp.split():
48
- if v[0] == '!':
49
- if self._venv is not None and self._venv == v[1:]:
50
- omit = True
51
- break
52
+
53
+ if self.VENV_MAGIC in l:
54
+ lp, _, rp = l.partition(self.VENV_MAGIC)
55
+ rp = rp.partition('#')[0]
56
+ for v in rp.split():
57
+ if v[0] == '!':
58
+ if self._venv is not None and self._venv == v[1:]:
59
+ omit = True
60
+ break
61
+ else:
62
+ raise NotImplementedError
63
+
64
+ if (
65
+ not omit and
66
+ (ops := self._only_pats) is not None and
67
+ not l.strip().startswith('-')
68
+ ):
69
+ try:
70
+ pr = parse_requirement(l.split('#')[0].strip())
71
+ except RequiresParserSyntaxError:
72
+ pass
52
73
  else:
53
- raise NotImplementedError
74
+ if not any(op.fullmatch(pr.name) for op in ops):
75
+ omit = True
54
76
 
55
77
  if omit:
56
78
  out_lines.append('# OMITTED: ' + l)
omdev/pyproject/venvs.py CHANGED
@@ -1,6 +1,7 @@
1
1
  # ruff: noqa: UP006 UP007 UP045
2
2
  import glob
3
3
  import os.path
4
+ import re
4
5
  import typing as ta
5
6
 
6
7
  from omlish.lite.cached import async_cached_nullary
@@ -42,7 +43,13 @@ class Venv:
42
43
 
43
44
  @cached_nullary
44
45
  def _iv(self) -> InterpVenv:
45
- rr = RequirementsRewriter(self._name)
46
+ rr = RequirementsRewriter(
47
+ venv=self._name,
48
+ only_pats=(
49
+ [re.compile(p) for p in self._cfg.requires_pats]
50
+ if self._cfg.requires_pats is not None else None
51
+ ),
52
+ )
46
53
 
47
54
  return InterpVenv(
48
55
  self.dir_name,
omdev/scripts/ci.py CHANGED
@@ -4718,8 +4718,6 @@ class _JustMaybe(_Maybe[T]):
4718
4718
  __slots__ = ('_v', '_hash')
4719
4719
 
4720
4720
  def __init__(self, v: T) -> None:
4721
- super().__init__()
4722
-
4723
4721
  self._v = v
4724
4722
 
4725
4723
  @property
omdev/scripts/interp.py CHANGED
@@ -2630,8 +2630,6 @@ class _JustMaybe(_Maybe[T]):
2630
2630
  __slots__ = ('_v', '_hash')
2631
2631
 
2632
2632
  def __init__(self, v: T) -> None:
2633
- super().__init__()
2634
-
2635
2633
  self._v = v
2636
2634
 
2637
2635
  @property
@@ -896,8 +896,6 @@ class _JustMaybe(_Maybe[T]):
896
896
  __slots__ = ('_v', '_hash')
897
897
 
898
898
  def __init__(self, v: T) -> None:
899
- super().__init__()
900
-
901
899
  self._v = v
902
900
 
903
901
  @property
@@ -27,6 +27,7 @@ See:
27
27
  """
28
28
  import abc
29
29
  import argparse
30
+ import ast
30
31
  import asyncio
31
32
  import asyncio.base_subprocess
32
33
  import asyncio.subprocess
@@ -137,6 +138,11 @@ LoggingExcInfo = ta.Union[BaseException, LoggingExcInfoTuple] # ta.TypeAlias
137
138
  LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
138
139
  LoggingContextInfo = ta.Any # ta.TypeAlias
139
140
 
141
+ # ../packaging/requires.py
142
+ RequiresMarkerVar = ta.Union['RequiresVariable', 'RequiresValue'] # ta.TypeAlias
143
+ RequiresMarkerAtom = ta.Union['RequiresMarkerItem', ta.Sequence['RequiresMarkerAtom']] # ta.TypeAlias
144
+ RequiresMarkerList = ta.Sequence[ta.Union['RequiresMarkerList', 'RequiresMarkerAtom', str]] # ta.TypeAlias
145
+
140
146
  # ../../omlish/asyncs/asyncio/timeouts.py
141
147
  AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
142
148
 
@@ -5221,8 +5227,6 @@ class _JustMaybe(_Maybe[T]):
5221
5227
  __slots__ = ('_v', '_hash')
5222
5228
 
5223
5229
  def __init__(self, v: T) -> None:
5224
- super().__init__()
5225
-
5226
5230
  self._v = v
5227
5231
 
5228
5232
  @property
@@ -6050,6 +6054,488 @@ class Interp:
6050
6054
  version: InterpVersion
6051
6055
 
6052
6056
 
6057
+ ########################################
6058
+ # ../../packaging/requires.py
6059
+ # Copyright (c) Donald Stufft and individual contributors.
6060
+ # All rights reserved.
6061
+ #
6062
+ # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
6063
+ # following conditions are met:
6064
+ #
6065
+ # 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
6066
+ # following disclaimer.
6067
+ #
6068
+ # 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
6069
+ # following disclaimer in the documentation and/or other materials provided with the distribution.
6070
+ #
6071
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
6072
+ # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
6073
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
6074
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
6075
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
6076
+ # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
6077
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This file is dual licensed under the terms of the
6078
+ # Apache License, Version 2.0, and the BSD License. See the LICENSE file in the root of this repository for complete
6079
+ # details.
6080
+ # https://github.com/pypa/packaging/blob/cf2cbe2aec28f87c6228a6fb136c27931c9af407/src/packaging/_parser.py#L65
6081
+
6082
+
6083
+ ##
6084
+
6085
+
6086
+ @dc.dataclass()
6087
+ class RequiresToken:
6088
+ name: str
6089
+ text: str
6090
+ position: int
6091
+
6092
+
6093
+ class RequiresParserSyntaxError(Exception):
6094
+ def __init__(
6095
+ self,
6096
+ message: str,
6097
+ *,
6098
+ source: str,
6099
+ span: ta.Tuple[int, int],
6100
+ ) -> None:
6101
+ self.span = span
6102
+ self.message = message
6103
+ self.source = source
6104
+
6105
+ super().__init__()
6106
+
6107
+ def __str__(self) -> str:
6108
+ marker = ' ' * self.span[0] + '~' * (self.span[1] - self.span[0]) + '^'
6109
+ return '\n '.join([self.message, self.source, marker])
6110
+
6111
+
6112
+ REQUIRES_DEFAULT_RULES: ta.Dict[str, ta.Union[str, ta.Pattern[str]]] = {
6113
+ 'LEFT_PARENTHESIS': r'\(',
6114
+ 'RIGHT_PARENTHESIS': r'\)',
6115
+ 'LEFT_BRACKET': r'\[',
6116
+ 'RIGHT_BRACKET': r'\]',
6117
+ 'SEMICOLON': r';',
6118
+ 'COMMA': r',',
6119
+ 'QUOTED_STRING': re.compile(
6120
+ r"""
6121
+ (
6122
+ ('[^']*')
6123
+ |
6124
+ ("[^"]*")
6125
+ )
6126
+ """,
6127
+ re.VERBOSE,
6128
+ ),
6129
+ 'OP': r'(===|==|~=|!=|<=|>=|<|>)',
6130
+ 'BOOLOP': r'\b(or|and)\b',
6131
+ 'IN': r'\bin\b',
6132
+ 'NOT': r'\bnot\b',
6133
+ 'VARIABLE': re.compile(
6134
+ r"""
6135
+ \b(
6136
+ python_version
6137
+ |python_full_version
6138
+ |os[._]name
6139
+ |sys[._]platform
6140
+ |platform_(release|system)
6141
+ |platform[._](version|machine|python_implementation)
6142
+ |python_implementation
6143
+ |implementation_(name|version)
6144
+ |extra
6145
+ )\b
6146
+ """,
6147
+ re.VERBOSE,
6148
+ ),
6149
+ 'SPECIFIER': re.compile(
6150
+ Specifier._operator_regex_str + Specifier._version_regex_str, # noqa
6151
+ re.VERBOSE | re.IGNORECASE,
6152
+ ),
6153
+ 'AT': r'\@',
6154
+ 'URL': r'[^ \t]+',
6155
+ 'IDENTIFIER': r'\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b',
6156
+ 'VERSION_PREFIX_TRAIL': r'\.\*',
6157
+ 'VERSION_LOCAL_LABEL_TRAIL': r'\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*',
6158
+ 'WS': r'[ \t]+',
6159
+ 'END': r'$',
6160
+ }
6161
+
6162
+
6163
+ class RequiresTokenizer:
6164
+ def __init__(
6165
+ self,
6166
+ source: str,
6167
+ *,
6168
+ rules: ta.Dict[str, ta.Union[str, ta.Pattern[str]]],
6169
+ ) -> None:
6170
+ super().__init__()
6171
+
6172
+ self.source = source
6173
+ self.rules: ta.Dict[str, ta.Pattern[str]] = {name: re.compile(pattern) for name, pattern in rules.items()}
6174
+ self.next_token: ta.Optional[RequiresToken] = None
6175
+ self.position = 0
6176
+
6177
+ def consume(self, name: str) -> None:
6178
+ if self.check(name):
6179
+ self.read()
6180
+
6181
+ def check(self, name: str, *, peek: bool = False) -> bool:
6182
+ check.state(self.next_token is None, f'Cannot check for {name!r}, already have {self.next_token!r}')
6183
+ check.state(name in self.rules, f'Unknown token name: {name!r}')
6184
+
6185
+ expression = self.rules[name]
6186
+
6187
+ match = expression.match(self.source, self.position)
6188
+ if match is None:
6189
+ return False
6190
+ if not peek:
6191
+ self.next_token = RequiresToken(name, match[0], self.position)
6192
+ return True
6193
+
6194
+ def expect(self, name: str, *, expected: str) -> RequiresToken:
6195
+ if not self.check(name):
6196
+ raise self.raise_syntax_error(f'Expected {expected}')
6197
+ return self.read()
6198
+
6199
+ def read(self) -> RequiresToken:
6200
+ token = self.next_token
6201
+ check.state(token is not None)
6202
+
6203
+ self.position += len(check.not_none(token).text)
6204
+ self.next_token = None
6205
+
6206
+ return check.not_none(token)
6207
+
6208
+ def raise_syntax_error(
6209
+ self,
6210
+ message: str,
6211
+ *,
6212
+ span_start: ta.Optional[int] = None,
6213
+ span_end: ta.Optional[int] = None,
6214
+ ) -> ta.NoReturn:
6215
+ span = (
6216
+ self.position if span_start is None else span_start,
6217
+ self.position if span_end is None else span_end,
6218
+ )
6219
+ raise RequiresParserSyntaxError(
6220
+ message,
6221
+ source=self.source,
6222
+ span=span,
6223
+ )
6224
+
6225
+ @contextlib.contextmanager
6226
+ def enclosing_tokens(self, open_token: str, close_token: str, *, around: str) -> ta.Iterator[None]:
6227
+ if self.check(open_token):
6228
+ open_position = self.position
6229
+ self.read()
6230
+ else:
6231
+ open_position = None
6232
+
6233
+ yield
6234
+
6235
+ if open_position is None:
6236
+ return
6237
+
6238
+ if not self.check(close_token):
6239
+ self.raise_syntax_error(
6240
+ f'Expected matching {close_token} for {open_token}, after {around}',
6241
+ span_start=open_position,
6242
+ )
6243
+
6244
+ self.read()
6245
+
6246
+
6247
+ @dc.dataclass(frozen=True)
6248
+ class RequiresNode:
6249
+ value: str
6250
+
6251
+ def __str__(self) -> str:
6252
+ return self.value
6253
+
6254
+ def __repr__(self) -> str:
6255
+ return f"<{self.__class__.__name__}('{self}')>"
6256
+
6257
+ def serialize(self) -> str:
6258
+ raise NotImplementedError
6259
+
6260
+
6261
+ @dc.dataclass(frozen=True)
6262
+ class RequiresVariable(RequiresNode):
6263
+ def serialize(self) -> str:
6264
+ return str(self)
6265
+
6266
+
6267
+ @dc.dataclass(frozen=True)
6268
+ class RequiresValue(RequiresNode):
6269
+ def serialize(self) -> str:
6270
+ return f'"{self}"'
6271
+
6272
+
6273
+ @dc.dataclass(frozen=True)
6274
+ class RequiresOp(RequiresNode):
6275
+ def serialize(self) -> str:
6276
+ return str(self)
6277
+
6278
+
6279
+ class RequiresMarkerItem(ta.NamedTuple):
6280
+ l: ta.Union[RequiresVariable, RequiresValue]
6281
+ op: RequiresOp
6282
+ r: ta.Union[RequiresVariable, RequiresValue]
6283
+
6284
+
6285
+ class ParsedRequirement(ta.NamedTuple):
6286
+ name: str
6287
+ url: str
6288
+ extras: ta.List[str]
6289
+ specifier: str
6290
+ marker: ta.Optional[RequiresMarkerList]
6291
+
6292
+
6293
+ def parse_requirement(source: str) -> ParsedRequirement:
6294
+ return _parse_requirement(RequiresTokenizer(source, rules=REQUIRES_DEFAULT_RULES))
6295
+
6296
+
6297
+ def _parse_requirement(tokenizer: RequiresTokenizer) -> ParsedRequirement:
6298
+ tokenizer.consume('WS')
6299
+
6300
+ name_token = tokenizer.expect('IDENTIFIER', expected='package name at the start of dependency specifier')
6301
+ name = name_token.text
6302
+ tokenizer.consume('WS')
6303
+
6304
+ extras = _parse_requires_extras(tokenizer)
6305
+ tokenizer.consume('WS')
6306
+
6307
+ url, specifier, marker = _parse_requirement_details(tokenizer)
6308
+ tokenizer.expect('END', expected='end of dependency specifier')
6309
+
6310
+ return ParsedRequirement(name, url, extras, specifier, marker)
6311
+
6312
+
6313
+ def _parse_requirement_details(tokenizer: RequiresTokenizer) -> ta.Tuple[str, str, ta.Optional[RequiresMarkerList]]:
6314
+ specifier = ''
6315
+ url = ''
6316
+ marker = None
6317
+
6318
+ if tokenizer.check('AT'):
6319
+ tokenizer.read()
6320
+ tokenizer.consume('WS')
6321
+
6322
+ url_start = tokenizer.position
6323
+ url = tokenizer.expect('URL', expected='URL after @').text
6324
+ if tokenizer.check('END', peek=True):
6325
+ return (url, specifier, marker)
6326
+
6327
+ tokenizer.expect('WS', expected='whitespace after URL')
6328
+
6329
+ # The input might end after whitespace.
6330
+ if tokenizer.check('END', peek=True):
6331
+ return (url, specifier, marker)
6332
+
6333
+ marker = _parse_requirement_marker(
6334
+ tokenizer, span_start=url_start, after='URL and whitespace',
6335
+ )
6336
+ else:
6337
+ specifier_start = tokenizer.position
6338
+ specifier = _parse_requires_specifier(tokenizer)
6339
+ tokenizer.consume('WS')
6340
+
6341
+ if tokenizer.check('END', peek=True):
6342
+ return (url, specifier, marker)
6343
+
6344
+ marker = _parse_requirement_marker(
6345
+ tokenizer,
6346
+ span_start=specifier_start,
6347
+ after=(
6348
+ 'version specifier'
6349
+ if specifier
6350
+ else 'name and no valid version specifier'
6351
+ ),
6352
+ )
6353
+
6354
+ return (url, specifier, marker)
6355
+
6356
+
6357
+ def _parse_requirement_marker(
6358
+ tokenizer: RequiresTokenizer, *, span_start: int, after: str,
6359
+ ) -> RequiresMarkerList:
6360
+ if not tokenizer.check('SEMICOLON'):
6361
+ tokenizer.raise_syntax_error(
6362
+ f'Expected end or semicolon (after {after})',
6363
+ span_start=span_start,
6364
+ )
6365
+ tokenizer.read()
6366
+
6367
+ marker = _parse_requires_marker(tokenizer)
6368
+ tokenizer.consume('WS')
6369
+
6370
+ return marker
6371
+
6372
+
6373
+ def _parse_requires_extras(tokenizer: RequiresTokenizer) -> ta.List[str]:
6374
+ if not tokenizer.check('LEFT_BRACKET', peek=True):
6375
+ return []
6376
+
6377
+ with tokenizer.enclosing_tokens(
6378
+ 'LEFT_BRACKET',
6379
+ 'RIGHT_BRACKET',
6380
+ around='extras',
6381
+ ):
6382
+ tokenizer.consume('WS')
6383
+ extras = _parse_requires_extras_list(tokenizer)
6384
+ tokenizer.consume('WS')
6385
+
6386
+ return extras
6387
+
6388
+
6389
+ def _parse_requires_extras_list(tokenizer: RequiresTokenizer) -> ta.List[str]:
6390
+ extras: ta.List[str] = []
6391
+
6392
+ if not tokenizer.check('IDENTIFIER'):
6393
+ return extras
6394
+
6395
+ extras.append(tokenizer.read().text)
6396
+
6397
+ while True:
6398
+ tokenizer.consume('WS')
6399
+ if tokenizer.check('IDENTIFIER', peek=True):
6400
+ tokenizer.raise_syntax_error('Expected comma between extra names')
6401
+ elif not tokenizer.check('COMMA'):
6402
+ break
6403
+
6404
+ tokenizer.read()
6405
+ tokenizer.consume('WS')
6406
+
6407
+ extra_token = tokenizer.expect('IDENTIFIER', expected='extra name after comma')
6408
+ extras.append(extra_token.text)
6409
+
6410
+ return extras
6411
+
6412
+
6413
+ def _parse_requires_specifier(tokenizer: RequiresTokenizer) -> str:
6414
+ with tokenizer.enclosing_tokens(
6415
+ 'LEFT_PARENTHESIS',
6416
+ 'RIGHT_PARENTHESIS',
6417
+ around='version specifier',
6418
+ ):
6419
+ tokenizer.consume('WS')
6420
+ parsed_specifiers = _parse_requires_version_many(tokenizer)
6421
+ tokenizer.consume('WS')
6422
+
6423
+ return parsed_specifiers
6424
+
6425
+
6426
+ def _parse_requires_version_many(tokenizer: RequiresTokenizer) -> str:
6427
+ parsed_specifiers = ''
6428
+ while tokenizer.check('SPECIFIER'):
6429
+ span_start = tokenizer.position
6430
+ parsed_specifiers += tokenizer.read().text
6431
+ if tokenizer.check('VERSION_PREFIX_TRAIL', peek=True):
6432
+ tokenizer.raise_syntax_error(
6433
+ '.* suffix can only be used with `==` or `!=` operators',
6434
+ span_start=span_start,
6435
+ span_end=tokenizer.position + 1,
6436
+ )
6437
+ if tokenizer.check('VERSION_LOCAL_LABEL_TRAIL', peek=True):
6438
+ tokenizer.raise_syntax_error(
6439
+ 'Local version label can only be used with `==` or `!=` operators',
6440
+ span_start=span_start,
6441
+ span_end=tokenizer.position,
6442
+ )
6443
+ tokenizer.consume('WS')
6444
+ if not tokenizer.check('COMMA'):
6445
+ break
6446
+ parsed_specifiers += tokenizer.read().text
6447
+ tokenizer.consume('WS')
6448
+
6449
+ return parsed_specifiers
6450
+
6451
+
6452
+ def parse_requires_marker(source: str) -> RequiresMarkerList:
6453
+ return _parse_requires_full_marker(RequiresTokenizer(source, rules=REQUIRES_DEFAULT_RULES))
6454
+
6455
+
6456
+ def _parse_requires_full_marker(tokenizer: RequiresTokenizer) -> RequiresMarkerList:
6457
+ retval = _parse_requires_marker(tokenizer)
6458
+ tokenizer.expect('END', expected='end of marker expression')
6459
+ return retval
6460
+
6461
+
6462
+ def _parse_requires_marker(tokenizer: RequiresTokenizer) -> RequiresMarkerList:
6463
+ expression = [_parse_requires_marker_atom(tokenizer)]
6464
+ while tokenizer.check('BOOLOP'):
6465
+ token = tokenizer.read()
6466
+ expr_right = _parse_requires_marker_atom(tokenizer)
6467
+ expression.extend((token.text, expr_right))
6468
+ return expression
6469
+
6470
+
6471
+ def _parse_requires_marker_atom(tokenizer: RequiresTokenizer) -> RequiresMarkerAtom:
6472
+ tokenizer.consume('WS')
6473
+ if tokenizer.check('LEFT_PARENTHESIS', peek=True):
6474
+ with tokenizer.enclosing_tokens(
6475
+ 'LEFT_PARENTHESIS',
6476
+ 'RIGHT_PARENTHESIS',
6477
+ around='marker expression',
6478
+ ):
6479
+ tokenizer.consume('WS')
6480
+ marker: RequiresMarkerAtom = _parse_requires_marker(tokenizer)
6481
+ tokenizer.consume('WS')
6482
+ else:
6483
+ marker = _parse_requires_marker_item(tokenizer)
6484
+ tokenizer.consume('WS')
6485
+ return marker
6486
+
6487
+
6488
+ def _parse_requires_marker_item(tokenizer: RequiresTokenizer) -> RequiresMarkerItem:
6489
+ tokenizer.consume('WS')
6490
+ marker_var_left = _parse_requires_marker_var(tokenizer)
6491
+ tokenizer.consume('WS')
6492
+ marker_op = _parse_requires_marker_op(tokenizer)
6493
+ tokenizer.consume('WS')
6494
+ marker_var_right = _parse_requires_marker_var(tokenizer)
6495
+ tokenizer.consume('WS')
6496
+ return RequiresMarkerItem(marker_var_left, marker_op, marker_var_right)
6497
+
6498
+
6499
+ def _parse_requires_marker_var(tokenizer: RequiresTokenizer) -> RequiresMarkerVar:
6500
+ if tokenizer.check('VARIABLE'):
6501
+ return process_requires_env_var(tokenizer.read().text.replace('.', '_'))
6502
+ elif tokenizer.check('QUOTED_STRING'):
6503
+ return process_requires_python_str(tokenizer.read().text)
6504
+ else:
6505
+ tokenizer.raise_syntax_error(message='Expected a marker variable or quoted string')
6506
+ raise RuntimeError # noqa
6507
+
6508
+
6509
+ def process_requires_env_var(env_var: str) -> RequiresVariable:
6510
+ if env_var in ('platform_python_implementation', 'python_implementation'):
6511
+ return RequiresVariable('platform_python_implementation')
6512
+ else:
6513
+ return RequiresVariable(env_var)
6514
+
6515
+
6516
+ def process_requires_python_str(python_str: str) -> RequiresValue:
6517
+ value = ast.literal_eval(python_str)
6518
+ return RequiresValue(str(value))
6519
+
6520
+
6521
+ def _parse_requires_marker_op(tokenizer: RequiresTokenizer) -> RequiresOp:
6522
+ if tokenizer.check('IN'):
6523
+ tokenizer.read()
6524
+ return RequiresOp('in')
6525
+ elif tokenizer.check('NOT'):
6526
+ tokenizer.read()
6527
+ tokenizer.expect('WS', expected="whitespace after 'not'")
6528
+ tokenizer.expect('IN', expected="'in' after 'not'")
6529
+ return RequiresOp('not in')
6530
+ elif tokenizer.check('OP'):
6531
+ return RequiresOp(tokenizer.read().text)
6532
+ else:
6533
+ return tokenizer.raise_syntax_error(
6534
+ 'Expected marker operator, one of '
6535
+ '<=, <, !=, ==, >=, >, ~=, ===, in, not in',
6536
+ )
6537
+
6538
+
6053
6539
  ########################################
6054
6540
  # ../../../omlish/asyncs/asyncio/timeouts.py
6055
6541
 
@@ -9740,11 +10226,14 @@ log = get_module_logger(globals()) # noqa
9740
10226
  class RequirementsRewriter:
9741
10227
  def __init__(
9742
10228
  self,
10229
+ *,
9743
10230
  venv: ta.Optional[str] = None,
10231
+ only_pats: ta.Optional[ta.Sequence[re.Pattern]] = None,
9744
10232
  ) -> None:
9745
10233
  super().__init__()
9746
10234
 
9747
10235
  self._venv = venv
10236
+ self._only_pats = only_pats
9748
10237
 
9749
10238
  @cached_nullary
9750
10239
  def _tmp_dir(self) -> str:
@@ -9760,17 +10249,32 @@ class RequirementsRewriter:
9760
10249
  out_lines = []
9761
10250
 
9762
10251
  for l in in_lines:
9763
- if self.VENV_MAGIC in l:
9764
- lp, _, rp = l.partition(self.VENV_MAGIC)
9765
- rp = rp.partition('#')[0]
10252
+ if l.split('#')[0].strip():
9766
10253
  omit = False
9767
- for v in rp.split():
9768
- if v[0] == '!':
9769
- if self._venv is not None and self._venv == v[1:]:
9770
- omit = True
9771
- break
10254
+
10255
+ if self.VENV_MAGIC in l:
10256
+ lp, _, rp = l.partition(self.VENV_MAGIC)
10257
+ rp = rp.partition('#')[0]
10258
+ for v in rp.split():
10259
+ if v[0] == '!':
10260
+ if self._venv is not None and self._venv == v[1:]:
10261
+ omit = True
10262
+ break
10263
+ else:
10264
+ raise NotImplementedError
10265
+
10266
+ if (
10267
+ not omit and
10268
+ (ops := self._only_pats) is not None and
10269
+ not l.strip().startswith('-')
10270
+ ):
10271
+ try:
10272
+ pr = parse_requirement(l.split('#')[0].strip())
10273
+ except RequiresParserSyntaxError:
10274
+ pass
9772
10275
  else:
9773
- raise NotImplementedError
10276
+ if not any(op.fullmatch(pr.name) for op in ops):
10277
+ omit = True
9774
10278
 
9775
10279
  if omit:
9776
10280
  out_lines.append('# OMITTED: ' + l)
@@ -11076,6 +11580,7 @@ def get_default_interp_resolver() -> InterpResolver:
11076
11580
  class InterpVenvConfig:
11077
11581
  interp: ta.Optional[str] = None
11078
11582
  requires: ta.Optional[ta.Sequence[str]] = None
11583
+ requires_pats: ta.Optional[ta.Sequence[str]] = None
11079
11584
  use_uv: ta.Optional[bool] = None
11080
11585
 
11081
11586
 
@@ -11300,7 +11805,13 @@ class Venv:
11300
11805
 
11301
11806
  @cached_nullary
11302
11807
  def _iv(self) -> InterpVenv:
11303
- rr = RequirementsRewriter(self._name)
11808
+ rr = RequirementsRewriter(
11809
+ venv=self._name,
11810
+ only_pats=(
11811
+ [re.compile(p) for p in self._cfg.requires_pats]
11812
+ if self._cfg.requires_pats is not None else None
11813
+ ),
11814
+ )
11304
11815
 
11305
11816
  return InterpVenv(
11306
11817
  self.dir_name,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omdev
3
- Version: 0.0.0.dev441
3
+ Version: 0.0.0.dev443
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev441
17
+ Requires-Dist: omlish==0.0.0.dev443
18
18
  Provides-Extra: all
19
19
  Requires-Dist: black~=25.1; extra == "all"
20
20
  Requires-Dist: pycparser~=2.23; extra == "all"
@@ -159,7 +159,7 @@ omdev/interp/inject.py,sha256=MOppFILGFCp2f4-2uwmH5fN-7erHI3RyMQJaG0kw0Gc,1569
159
159
  omdev/interp/inspect.py,sha256=GLjqRkmNr3H0CZsiFqizM_2yfUL7hSD-BGYysNcVi_Q,3018
160
160
  omdev/interp/resolvers.py,sha256=9ExwP0wcQ4mzyTLNurSG4Dg1AQ_IqLfR2ZyqR4VRANE,2590
161
161
  omdev/interp/types.py,sha256=Pr0wrVpNasoCw-ThEvKC5LG30Civ7YJ4EONwrwBLpy0,2516
162
- omdev/interp/venvs.py,sha256=a9lL0C84kNQoofSc7qa59Kgr_KMuyR9hUruKXbu1p98,3306
162
+ omdev/interp/venvs.py,sha256=KAnkHGP8jV8KmiarRl2wuMbya-CEgyN6hEM68irGOxs,3362
163
163
  omdev/interp/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
164
164
  omdev/interp/providers/base.py,sha256=zMtzMJIDvaI0q1gtsZTCvFiTc7qdhGbUcXRs6LCBmgo,1333
165
165
  omdev/interp/providers/inject.py,sha256=NSDFBQVD3ZR9Mf162XB9_VvTUAXGCRhPcrjVlYcFDJk,857
@@ -208,7 +208,7 @@ omdev/oci/pack/unpacking.py,sha256=tVYw8REKuYd4ciGXwMmXxlp4CRLHdET_gB9ShYh1d_M,6
208
208
  omdev/packaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
209
209
  omdev/packaging/marshal.py,sha256=nr7wN-Pi3FGAuh3HaladzDgOgyQl1-vtmxJ_AUOo4d8,2355
210
210
  omdev/packaging/names.py,sha256=-orp16m20gSFeKRiGkRNyqFVV4S1y_Djvjdq_5hNwpY,2533
211
- omdev/packaging/requires.py,sha256=rgEeJh8shqnFCPBtG_3Tu0vASXsvwK26ikJauFcrz0I,15691
211
+ omdev/packaging/requires.py,sha256=nn4ff8mlgfsdZooKzQjhASFS7mMVUCCmcb-150F0Lcg,15739
212
212
  omdev/packaging/revisions.py,sha256=tLNDVtPiygB8mSxwp-kDxeEHhpBzD0jayndjsgUOfkM,5055
213
213
  omdev/packaging/specifiers.py,sha256=t6UhvSlmwHmSvqqPb4XnCq9H41HiqeAqRvCUy2DF_P8,17487
214
214
  omdev/packaging/versions.py,sha256=SBh4LYfBYVi8SXMoWcOWY-Y3llOF-6cxayVaIM1NXMs,12371
@@ -267,19 +267,19 @@ omdev/pyproject/cli.py,sha256=Umsu2bcJUYeeVXICaZFhKckUBT6VWuYDL4htgCGGQIs,8749
267
267
  omdev/pyproject/configs.py,sha256=baNRwHtUW8S8DKCxuKlMbV3Gduujd1PyNURxQ48Nnxk,2813
268
268
  omdev/pyproject/inject.py,sha256=Von8_8ofkITLoCEwDHNRAwY0AEdFQg7r2ILS8kcTMuY,325
269
269
  omdev/pyproject/pkg.py,sha256=CcWBhsOgim8MYe5ryKDGB8ClRIyiCX6YmYUHRbQ5q_Y,14964
270
- omdev/pyproject/reqs.py,sha256=LrGr393-yqKbd2K_D-NePrvUIhMDjPG_1cEyQ3qesAM,2480
271
- omdev/pyproject/venvs.py,sha256=4JIAlNGWNHm4U9OgqG2f1fNwbqEJgQc34bhp9_46MT0,1980
270
+ omdev/pyproject/reqs.py,sha256=DU7NBNpFTjU06VgqRYZj5jZRQxpIWbzL9q9Vm13_o0o,3317
271
+ omdev/pyproject/venvs.py,sha256=PNgfVrGlw9NFKJgUyzyWH5H5nAIzUDPTHRVUNBM0bKs,2187
272
272
  omdev/pyproject/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
273
273
  omdev/pyproject/resources/docker-dev.sh,sha256=DHkz5D18jok_oDolfg2mqrvGRWFoCe9GQo04dR1czcc,838
274
274
  omdev/pyproject/resources/python.sh,sha256=rFaN4SiJ9hdLDXXsDTwugI6zsw6EPkgYMmtacZeTbvw,749
275
275
  omdev/scripts/__init__.py,sha256=MKCvUAEQwsIvwLixwtPlpBqmkMXLCnjjXyAXvVpDwVk,91
276
- omdev/scripts/ci.py,sha256=FfWskduSL49ktSMNd-e8eUn7K9BmmUtelP8S7g3DIbA,426405
277
- omdev/scripts/interp.py,sha256=xS894dtPAhB0Dbd4aPfSX5FK6OC2mZLD2N3pPZU4Xoo,168445
278
- omdev/scripts/pyproject.py,sha256=dMRhPLTKegqIha8ERBmZjdVYgIo57pokpGuXTZt2Zs4,332501
276
+ omdev/scripts/ci.py,sha256=0mb5CkL2y6_ZkMM5xXLEoVkPlh-c5_mb-Ezk45xYdCI,426377
277
+ omdev/scripts/interp.py,sha256=EaKZtDiopNF0i8Wf_DSYyM8c10aAGL41_1FUxCpSjxE,168417
278
+ omdev/scripts/pyproject.py,sha256=8V1bQYXcFRidO2y2adAiyX8VZyG4RMuFzW9DzSqOrgU,349087
279
279
  omdev/scripts/slowcat.py,sha256=PwdT-pg62imEEb6kcOozl9_YUi-4KopvjvzWT1OmGb0,2717
280
280
  omdev/scripts/tmpexec.py,sha256=t0nErDRALjTk7H0X8ADjZUIDFjlPNzOOokmjCjBHdzs,1431
281
281
  omdev/scripts/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
282
- omdev/scripts/lib/inject.py,sha256=W-9w85457SPmhzUKRZqLIkULh3arMNto-mdzk5PFlkY,53703
282
+ omdev/scripts/lib/inject.py,sha256=dregGw2IK_yBHR8rUKamV-sRx4YW_NUZAy-xrnfyie0,53675
283
283
  omdev/scripts/lib/logs.py,sha256=zRZYc-P9B0-uSETpIXNkNmb874jVvTfKtmUSJ47yZFw,63073
284
284
  omdev/scripts/lib/marshal.py,sha256=DOsUKJ1U3mwsNN1Et20cqJUBlzZZcmAv1m-zxMKHYEQ,46835
285
285
  omdev/tokens/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -323,9 +323,9 @@ omdev/tools/jsonview/resources/jsonview.js,sha256=faDvXDOXKvEvjOuIlz4D3F2ReQXb_b
323
323
  omdev/tools/pawk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
324
324
  omdev/tools/pawk/__main__.py,sha256=VCqeRVnqT1RPEoIrqHFSu4PXVMg4YEgF4qCQm90-eRI,66
325
325
  omdev/tools/pawk/pawk.py,sha256=ao5mdrpiSU4AZ8mBozoEaV3UVlmVTnRG9wD9XP70MZE,11429
326
- omdev-0.0.0.dev441.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
327
- omdev-0.0.0.dev441.dist-info/METADATA,sha256=FTG4PsStGupIrFeNgYOgsFRaoF11I53zW83x4oB0pus,5100
328
- omdev-0.0.0.dev441.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
329
- omdev-0.0.0.dev441.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
330
- omdev-0.0.0.dev441.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
331
- omdev-0.0.0.dev441.dist-info/RECORD,,
326
+ omdev-0.0.0.dev443.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
327
+ omdev-0.0.0.dev443.dist-info/METADATA,sha256=HFE5R2igDgcjuDWBV73bWvVn73S95-YNrPIGMzCg-xQ,5100
328
+ omdev-0.0.0.dev443.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
329
+ omdev-0.0.0.dev443.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
330
+ omdev-0.0.0.dev443.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
331
+ omdev-0.0.0.dev443.dist-info/RECORD,,