omlish 0.0.0.dev324__py3-none-any.whl → 0.0.0.dev326__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.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev324'
2
- __revision__ = 'f4f05345419ff5a1267aac207d5269c82156e71a'
1
+ __version__ = '0.0.0.dev326'
2
+ __revision__ = 'c2dd50f058b892485bea3f5ed42bfb88c8439c3c'
3
3
 
4
4
 
5
5
  #
@@ -99,7 +99,7 @@ class Project(ProjectBase):
99
99
  'aiosqlite ~= 0.21',
100
100
  'asyncpg ~= 0.30',
101
101
 
102
- 'apsw ~= 3.49',
102
+ 'apsw ~= 3.50',
103
103
 
104
104
  'sqlean.py ~= 3.49',
105
105
 
omlish/diag/pycharm.py CHANGED
@@ -14,12 +14,12 @@ from .. import lang
14
14
  if ta.TYPE_CHECKING:
15
15
  import plistlib
16
16
 
17
- from .. import docker
17
+ from ..docker import all as docker
18
18
 
19
19
  else:
20
20
  plistlib = lang.proxy_import('plistlib')
21
21
 
22
- docker = lang.proxy_import('..docker')
22
+ docker = lang.proxy_import('..docker.all')
23
23
 
24
24
 
25
25
  ##
omlish/docker/all.py CHANGED
@@ -13,6 +13,10 @@ from .compose import ( # noqa
13
13
  get_compose_port,
14
14
  )
15
15
 
16
+ from .consts import ( # noqa
17
+ DOCKER_FOR_MAC_HOSTNAME,
18
+ )
19
+
16
20
  from .detect import ( # noqa
17
21
  DOCKER_HOST_PLATFORM_KEY,
18
22
  get_docker_host_platform,
@@ -66,7 +66,7 @@ class DepSkipPlugin:
66
66
  collector._getobj = _patched_getobj # type: ignore # noqa
67
67
 
68
68
 
69
- def register(
69
+ def regex_register(
70
70
  pm: pytest.PytestPluginManager,
71
71
  file_pats: ta.Iterable[str],
72
72
  imp_pats: ta.Iterable[str],
@@ -79,3 +79,22 @@ def register(
79
79
  [re.compile(fp) for fp in file_pats],
80
80
  [re.compile(ip) for ip in imp_pats],
81
81
  ))
82
+
83
+
84
+ def module_register(
85
+ pm: pytest.PytestPluginManager,
86
+ mods: ta.Iterable[str],
87
+ imp_mods: ta.Iterable[str],
88
+ ) -> None:
89
+ check.not_isinstance(mods, str)
90
+ check.not_isinstance(imp_mods, str)
91
+
92
+ for m in [*mods, *imp_mods]:
93
+ check.non_empty_str(m)
94
+ check.arg(all(p.isidentifier() for p in m.split('.')), m)
95
+
96
+ regex_register(
97
+ pm,
98
+ [rf'{re.escape(m.replace(".", "/"))}/.*\.py' for m in mods],
99
+ [rf'{re.escape(m.replace(".", "/"))}(\..*)?' for m in imp_mods],
100
+ )
@@ -5,6 +5,7 @@ TODO:
5
5
  - dynamic switching (skip docker if not running, skip online if not online, ...)
6
6
  """
7
7
  import dataclasses as dc
8
+ import enum
8
9
  import typing as ta
9
10
 
10
11
  import pytest
@@ -21,20 +22,27 @@ Configable: ta.TypeAlias = pytest.FixtureRequest | pytest.Config
21
22
  ##
22
23
 
23
24
 
25
+ class SwitchState(enum.Enum):
26
+ ENABLED = enum.auto()
27
+ DISABLED = enum.auto()
28
+ ONLY = enum.auto()
29
+ IF_SINGLE = enum.auto()
30
+
31
+
24
32
  @dc.dataclass(frozen=True, eq=False)
25
33
  class Switch:
26
34
  name: str
27
- _default_enabled: bool | ta.Callable[[], bool]
35
+ _default_state: SwitchState | ta.Callable[[pytest.Session], SwitchState]
28
36
 
29
37
  _: dc.KW_ONLY
30
38
 
31
39
  add_marks: ta.Sequence[ta.Any] | None = None
32
40
 
33
- def default_enabled(self) -> bool:
34
- if isinstance(e := self._default_enabled, bool):
41
+ def default_state(self, session: pytest.Session) -> SwitchState:
42
+ if isinstance(e := self._default_state, SwitchState):
35
43
  return e
36
44
  elif callable(e):
37
- return check.isinstance(e(), bool)
45
+ return check.isinstance(e(session), SwitchState)
38
46
  else:
39
47
  raise TypeError(e)
40
48
 
@@ -46,27 +54,27 @@ class Switch:
46
54
  SWITCHES: ta.Sequence[Switch] = [
47
55
  Switch(
48
56
  'name',
49
- docker.has_cli,
57
+ lambda _: SwitchState.ENABLED if docker.has_cli() else SwitchState.DISABLED,
50
58
  ),
51
59
 
52
60
  Switch(
53
61
  'docker-guest',
54
- docker.is_likely_in_docker,
62
+ lambda _: SwitchState.ENABLED if docker.is_likely_in_docker() else SwitchState.DISABLED,
55
63
  ),
56
64
 
57
65
  Switch(
58
66
  'online',
59
- True,
67
+ SwitchState.ENABLED,
60
68
  ),
61
69
 
62
70
  Switch(
63
71
  'integration',
64
- True,
72
+ SwitchState.ENABLED,
65
73
  ),
66
74
 
67
75
  Switch(
68
76
  'high-mem',
69
- True,
77
+ SwitchState.ENABLED,
70
78
  add_marks=[
71
79
  # https://pytest-xdist.readthedocs.io/en/latest/distribution.html
72
80
  pytest.mark.xdist_group('high-mem'),
@@ -76,7 +84,7 @@ SWITCHES: ta.Sequence[Switch] = [
76
84
 
77
85
  Switch(
78
86
  'slow',
79
- False,
87
+ SwitchState.IF_SINGLE,
80
88
  ),
81
89
  ]
82
90
 
@@ -88,12 +96,10 @@ SWITCHES_BY_ATTR: ta.Mapping[str, Switch] = col.make_map_by(lambda sw: sw.attr,
88
96
  ##
89
97
 
90
98
 
91
- SwitchState: ta.TypeAlias = bool | ta.Literal['only']
92
-
93
99
  SWITCH_STATE_OPT_PREFIXES: ta.Mapping[SwitchState, str] = {
94
- True: '--',
95
- False: '--no-',
96
- 'only': '--only-',
100
+ SwitchState.ENABLED: '--',
101
+ SwitchState.DISABLED: '--no-',
102
+ SwitchState.ONLY: '--only-',
97
103
  }
98
104
 
99
105
 
@@ -132,53 +138,99 @@ def get_specified_switches(obj: Configable) -> dict[Switch, SwitchState]:
132
138
  return ret
133
139
 
134
140
 
141
+ @dc.dataclass(frozen=True)
142
+ class ItemSwitches:
143
+ switches: frozenset[Switch]
144
+ not_switches: frozenset[Switch]
145
+
146
+
147
+ def get_item_switches(item: pytest.Item) -> ItemSwitches:
148
+ return ItemSwitches(
149
+ frozenset(sw for sw in SWITCHES if sw.attr in item.keywords),
150
+ frozenset(sw for sw in SWITCHES if ('not_' + sw.attr) in item.keywords),
151
+ )
152
+
153
+
135
154
  @register
136
155
  class SwitchesPlugin:
137
- def pytest_configure(self, config):
138
- for sw in SWITCHES:
139
- config.addinivalue_line('markers', f'{sw.attr}: mark test as {sw.attr}')
140
- config.addinivalue_line('markers', f'not_{sw.attr}: mark test as not {sw.attr}')
141
-
142
156
  def pytest_addoption(self, parser):
143
157
  for sw in SWITCHES:
144
158
  parser.addoption(f'--no-{sw.name}', action='store_true', default=False, help=f'disable {sw.name} tests')
145
159
  parser.addoption(f'--{sw.name}', action='store_true', default=False, help=f'enables {sw.name} tests')
146
160
  parser.addoption(f'--only-{sw.name}', action='store_true', default=False, help=f'enables only {sw.name} tests') # noqa
147
161
 
148
- def pytest_collection_modifyitems(self, config, items):
149
- switch_states: dict[Switch, SwitchState] = {
150
- **{
151
- sw: sw.default_enabled()
162
+ def pytest_configure(self, config):
163
+ for sw in SWITCHES:
164
+ config.addinivalue_line('markers', f'{sw.attr}: mark test as {sw.attr}')
165
+ config.addinivalue_line('markers', f'not_{sw.attr}: mark test as not {sw.attr}')
166
+
167
+ class _States:
168
+ def __init__(self, session: pytest.Session) -> None:
169
+ super().__init__()
170
+
171
+ self.session = session
172
+
173
+ self.default_states_by_switch: dict[Switch, SwitchState] = {
174
+ sw: sw.default_state(session)
152
175
  for sw in SWITCHES
153
- },
154
- **get_specified_switches(config),
155
- }
176
+ }
177
+
178
+ self.states_by_switch: dict[Switch, SwitchState] = {
179
+ **self.default_states_by_switch,
180
+ **get_specified_switches(session.config),
181
+ }
156
182
 
157
- inv_switch_states: dict[SwitchState, list[Switch]] = col.multi_map((st, sw) for sw, st in switch_states.items())
158
- true_switches = frozenset(inv_switch_states.get(True, ()))
159
- false_switches = frozenset(inv_switch_states.get(False, ()))
160
- only_switches = frozenset(inv_switch_states.get('only', ()))
183
+ self.switch_sets_by_state: dict[SwitchState, frozenset[Switch]] = {
184
+ st: frozenset(sws)
185
+ for st, sws in col.multi_map(
186
+ (st, sw)
187
+ for sw, st in self.states_by_switch.items()
188
+ ).items()
189
+ }
161
190
 
162
- def process(item):
163
- item_switches = {sw for sw in SWITCHES if sw.attr in item.keywords}
164
- item_not_switches = {sw for sw in SWITCHES if ('not_' + sw.attr) in item.keywords}
191
+ _states_key: ta.ClassVar[pytest.StashKey[_States]] = pytest.StashKey[_States]()
165
192
 
166
- for sw in item_switches:
193
+ def pytest_sessionstart(self, session):
194
+ session.stash[self._states_key] = self._States(session)
195
+
196
+ def pytest_collection_modifyitems(self, config, items):
197
+ def process_item(item):
198
+ state: SwitchesPlugin._States = item.session.stash[self._states_key]
199
+
200
+ item_switches = get_item_switches(item)
201
+
202
+ for sw in item_switches.switches:
167
203
  for mk in sw.add_marks or []:
168
204
  item.add_marker(mk)
169
205
 
170
- if only_switches:
171
- if not any(sw in only_switches for sw in item_switches):
172
- item.add_marker(pytest.mark.skip(reason=f'skipping switches {item_switches}'))
206
+ if (only_switches := state.switch_sets_by_state.get(SwitchState.ONLY)) is not None:
207
+ if not any(sw in only_switches for sw in item_switches.switches):
208
+ item.add_marker(pytest.mark.skip(
209
+ reason=f'skipping switches ({tuple(sw.name for sw in item_switches.switches)!r} (only)',
210
+ ))
173
211
  return
174
212
 
175
- for sw in item_switches:
176
- if sw in false_switches:
177
- item.add_marker(pytest.mark.skip(reason=f'skipping switches {sw}'))
213
+ disabled_switches = state.switch_sets_by_state.get(SwitchState.DISABLED, frozenset())
214
+ for sw in item_switches.switches:
215
+ if sw in disabled_switches:
216
+ item.add_marker(pytest.mark.skip(reason=f'skipping switch {sw.name!r} (disabled)'))
178
217
 
179
- for sw in item_not_switches:
180
- if sw in true_switches:
181
- item.add_marker(pytest.mark.skip(reason=f'skipping switches {sw}'))
218
+ enabled_switches = state.switch_sets_by_state.get(SwitchState.ENABLED, frozenset())
219
+ for sw in item_switches.not_switches:
220
+ if sw in enabled_switches:
221
+ item.add_marker(pytest.mark.skip(reason=f'skipping switch {sw.name!r} (not-enabled)'))
182
222
 
183
223
  for item in items:
184
- process(item)
224
+ process_item(item)
225
+
226
+ def pytest_collection_finish(self, session):
227
+ state: SwitchesPlugin._States = session.stash[self._states_key]
228
+
229
+ if_single_switches = state.switch_sets_by_state.get(SwitchState.IF_SINGLE, frozenset())
230
+
231
+ if len(session.items) > 1:
232
+ for item in session.items:
233
+ item_switches = get_item_switches(item)
234
+ for sw in item_switches.switches:
235
+ if sw in if_single_switches:
236
+ item.add_marker(pytest.mark.skip(reason=f'skipping switch {sw.name!r} (not-single)'))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish
3
- Version: 0.0.0.dev324
3
+ Version: 0.0.0.dev326
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -39,7 +39,7 @@ Requires-Dist: pymysql~=1.1; extra == "all"
39
39
  Requires-Dist: aiomysql~=0.2; extra == "all"
40
40
  Requires-Dist: aiosqlite~=0.21; extra == "all"
41
41
  Requires-Dist: asyncpg~=0.30; extra == "all"
42
- Requires-Dist: apsw~=3.49; extra == "all"
42
+ Requires-Dist: apsw~=3.50; extra == "all"
43
43
  Requires-Dist: sqlean.py~=3.49; extra == "all"
44
44
  Requires-Dist: duckdb~=1.3; extra == "all"
45
45
  Requires-Dist: markupsafe~=3.0; extra == "all"
@@ -87,7 +87,7 @@ Requires-Dist: pymysql~=1.1; extra == "sqldrivers"
87
87
  Requires-Dist: aiomysql~=0.2; extra == "sqldrivers"
88
88
  Requires-Dist: aiosqlite~=0.21; extra == "sqldrivers"
89
89
  Requires-Dist: asyncpg~=0.30; extra == "sqldrivers"
90
- Requires-Dist: apsw~=3.49; extra == "sqldrivers"
90
+ Requires-Dist: apsw~=3.50; extra == "sqldrivers"
91
91
  Requires-Dist: sqlean.py~=3.49; extra == "sqldrivers"
92
92
  Requires-Dist: duckdb~=1.3; extra == "sqldrivers"
93
93
  Provides-Extra: templates
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=orgsRvtpHu8tdhaCvlP9v3P495OJopYYiHKjK68WtWg,8587
2
- omlish/__about__.py,sha256=RIJDWJT7aTvq6WVukFxeI8leEd-msgKydBtuT4dvSqc,3478
2
+ omlish/__about__.py,sha256=UoJfm6_oqNZqXXNxGuPSwyPZfi4oLWZWRWwT6V3IWhw,3478
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=rer-TPOFDU6fYq_AWio_AmA-ckZ8JDY5shIzQ_yXfzA,8414
5
5
  omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
@@ -211,7 +211,7 @@ omlish/diag/lsof.py,sha256=DnowqvKYah-WCuBHS3DAcZCvlsWJdM9kYNFq97UZDDA,9127
211
211
  omlish/diag/procfs.py,sha256=KaGTAA2Gj8eEEp7MjClRe4aimwzd-HDABThFzvq2cBQ,9684
212
212
  omlish/diag/procstats.py,sha256=EJEe2Zc58ykBoTfqMXro7H52aQa_pd6uC2hsIPFceso,825
213
213
  omlish/diag/ps.py,sha256=b7ai9O4mGZliNFvBu6PdQfMmct4qpcMTygEf1ISHBLQ,1666
214
- omlish/diag/pycharm.py,sha256=Z2W-Viqw5xq08ZC4z36skpozfYw_qNNhWQx_GYr2D0k,4695
214
+ omlish/diag/pycharm.py,sha256=9Mgn5T2ZdlEUL3VV-GzVmCBs_ZtIpLwaUzP6pgHEUEo,4712
215
215
  omlish/diag/pydevd.py,sha256=UN55ZjkWLCVyHxE2CNRRYamuvSKfzWsn0D5oczRTXO4,7536
216
216
  omlish/diag/threads.py,sha256=1-x02VCDZ407gfbtXm1pWK-ubqhqfePm9PMqkHCVoqk,3642
217
217
  omlish/diag/_pycharm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -228,7 +228,7 @@ omlish/dispatch/functions.py,sha256=cwNzGIg2ZIalEgn9I03cnJVbMTHjWloyDTaowlO3UPs,
228
228
  omlish/dispatch/impls.py,sha256=K_okKvpZml4NkTHJmTVyMQSrIaIJcqTEgkreGwukaOw,1895
229
229
  omlish/dispatch/methods.py,sha256=GdVsC-Zo3N-OorE0mr3lFI70dATkD2aWqZ8TvExiR5k,9297
230
230
  omlish/docker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
231
- omlish/docker/all.py,sha256=xXRgJgLGPwAtr7bDMJ_Dp9jTfOwfGvohNhc6LsoELJc,514
231
+ omlish/docker/all.py,sha256=t5jBNZAzqCoB05-nwCuSZ6C3PBEBD6T3wsIJvIXJaRg,576
232
232
  omlish/docker/cli.py,sha256=gtb9kitVfGnd4cr587NsVVk8D5Ok5y5SAsqD_SwGrSA,2565
233
233
  omlish/docker/compose.py,sha256=4drmnGQzbkOFJ9B6XSg9rnXkJeZz1ETmdcMe1PE790U,1237
234
234
  omlish/docker/consts.py,sha256=wvwfUtEFrEWZKfREWqSMrx8xjjl8P5MNUSF6qzzgJHY,70
@@ -746,14 +746,14 @@ omlish/testing/pytest/inject/__init__.py,sha256=pdRKv1HcDmJ_yArKJbYITPXXZthRSGgB
746
746
  omlish/testing/pytest/inject/harness.py,sha256=_Qf7lLcYc_dpauYOE68u_a65jPCFWmQUYv9m_OOdNqs,5724
747
747
  omlish/testing/pytest/plugins/__init__.py,sha256=ys1zXrYrNm7Uo6YOIVJ6Bd3dQo6kv387k7MbTYlqZSI,467
748
748
  omlish/testing/pytest/plugins/_registry.py,sha256=IK04KlBgiOJxKAyCCgjpX2R-9tE-btalYJkgjLc8Te8,77
749
- omlish/testing/pytest/plugins/depskip.py,sha256=xithY-OMtjwhv8mcRNkv-WI_PSQtHldQ8H1s60MIXkk,2673
749
+ omlish/testing/pytest/plugins/depskip.py,sha256=9xsvf4Py31Cc3RMUBA-BcTkOstLmYsU1GA4iwNr3fPo,3209
750
750
  omlish/testing/pytest/plugins/logging.py,sha256=ZUy4sQOMYroZubUnjYe4_KC1kqukgBbA84VjO0NwfsA,390
751
751
  omlish/testing/pytest/plugins/managermarks.py,sha256=IzkspjT8-ToPIlHZs9cHt6RYbm51poJLjYVvkFPbJhE,1300
752
752
  omlish/testing/pytest/plugins/pydevd.py,sha256=5moE64LrNRV6gKRaeCuONDiwuh-4UFxJPWD9K9PR8GM,841
753
753
  omlish/testing/pytest/plugins/repeat.py,sha256=jiZCI-17042jBvUEbZCxNwqWtr7s3EhTBSeSHh_Uz4E,497
754
754
  omlish/testing/pytest/plugins/skips.py,sha256=eMir_n777Kk2BvOwjQzRROKL4iAMYKSHFwWCHJ-bdPI,1040
755
755
  omlish/testing/pytest/plugins/spacing.py,sha256=tzq-L-exegHe2BImumkYIsVcUwpUUhLJJOuSrsKDuCU,706
756
- omlish/testing/pytest/plugins/switches.py,sha256=t4kzdB_4Isp_cUtseKKMGHZ6cTriX3VGrzuqmNHCgMg,5210
756
+ omlish/testing/pytest/plugins/switches.py,sha256=z3TKe2NHBOaa_cmarWTRJo519iCNDff4e0XSfHkajHU,7365
757
757
  omlish/testing/pytest/plugins/utils.py,sha256=L5C622UXcA_AUKDcvyh5IMiRfqSGGz0McdhwZWvfMlU,261
758
758
  omlish/testing/pytest/plugins/asyncs/__init__.py,sha256=TTNhFmP_krug1973sq_bpWBTIvg68-1nbuVLSs92Z6k,41
759
759
  omlish/testing/pytest/plugins/asyncs/consts.py,sha256=0NOCkzV43dOu3u97BqYMQ4mPG8JuFncpWibkOZpCqX4,55
@@ -857,9 +857,9 @@ omlish/typedvalues/holder.py,sha256=ZTnHiw-K38ciOBLEdwgrltr7Xp8jjEs_0Lp69DH-G-o,
857
857
  omlish/typedvalues/marshal.py,sha256=hWHRLcrGav7lvXJDtb9bNI0ickl4SKPQ6F4BbTpqw3A,4219
858
858
  omlish/typedvalues/reflect.py,sha256=Ih1YgU-srUjsvBn_P7C66f73_VCvcwqE3ffeBnZBgt4,674
859
859
  omlish/typedvalues/values.py,sha256=ym46I-q2QJ_6l4UlERqv3yj87R-kp8nCKMRph0xQ3UA,1307
860
- omlish-0.0.0.dev324.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
861
- omlish-0.0.0.dev324.dist-info/METADATA,sha256=cvoRr807Bf7z8c0DJfoo46V0v17yj6akOnN34Pr6mZk,4416
862
- omlish-0.0.0.dev324.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
863
- omlish-0.0.0.dev324.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
864
- omlish-0.0.0.dev324.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
865
- omlish-0.0.0.dev324.dist-info/RECORD,,
860
+ omlish-0.0.0.dev326.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
861
+ omlish-0.0.0.dev326.dist-info/METADATA,sha256=TU88zs941JZBexTmeINDhh8lNdgCPbBLmdDZ8E2kV5I,4416
862
+ omlish-0.0.0.dev326.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
863
+ omlish-0.0.0.dev326.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
864
+ omlish-0.0.0.dev326.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
865
+ omlish-0.0.0.dev326.dist-info/RECORD,,