omdev 0.0.0.dev66__py3-none-any.whl → 0.0.0.dev67__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.

Potentially problematic release.


This version of omdev might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev66
3
+ Version: 0.0.0.dev67
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,7 +12,7 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: ~=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omlish ==0.0.0.dev66
15
+ Requires-Dist: omlish ==0.0.0.dev67
16
16
  Provides-Extra: all
17
17
  Requires-Dist: pycparser ~=2.22 ; extra == 'all'
18
18
  Requires-Dist: cffi ~=1.17 ; extra == 'all'
@@ -93,8 +93,6 @@ omdev/precheck/git.py,sha256=APC5Ln7x0zDrQiGPRWPsBcVJK3vWhbU-brqR5M63JQA,849
93
93
  omdev/precheck/lite.py,sha256=MLeDZP2UexNZzYTcSx4-LrhA97kCKn8tXrGkhsJb6I0,3649
94
94
  omdev/precheck/precheck.py,sha256=Boe3zbK0RXCGzw9H_OsyqJ4yMETuyrwy8P4UFAZQcTY,2477
95
95
  omdev/precheck/scripts.py,sha256=qq6MXkxgrYngPg5pWnXH4uRSuRkP3mFqbeml1UmvGBc,1265
96
- omdev/pycharm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
- omdev/pycharm/runhack.py,sha256=boK9a8SMvT9kPnJNtZJ5p1iA6OUlf3CV9buHLb8Zv7Q,33242
98
96
  omdev/pyproject/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
99
97
  omdev/pyproject/__main__.py,sha256=gn3Rl1aYPYdiTtEqa9ifi0t-e4ZwPY0vhJ4UXvYdJDY,165
100
98
  omdev/pyproject/cexts.py,sha256=x13piOOnNrYbA17qZLDVuR0p1sqhgEwpk4FtImX-klM,4281
@@ -122,9 +120,9 @@ omdev/tools/piptools.py,sha256=-jR5q3w4sHqntxCLExFCBNIARB788FUsAbJ62PK2sBU,2774
122
120
  omdev/tools/proftools.py,sha256=xKSm_yPoCnfsvS3iT9MblDqFMuZmGfI3_koGj8amMyU,145
123
121
  omdev/tools/rst.py,sha256=6dWk8QZHoGiLSuBw3TKsXZjjFK6wWBEtPi9krdCLKKg,977
124
122
  omdev/tools/sqlrepl.py,sha256=tmFZh80-xsGM62dyQ7_UGLebChrj7IHbIPYBWDJMgVk,5741
125
- omdev-0.0.0.dev66.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
126
- omdev-0.0.0.dev66.dist-info/METADATA,sha256=ccZeKMsEzkV49SBk06qnqoTHgOX5__5FdBsWmZC1X-k,1252
127
- omdev-0.0.0.dev66.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
128
- omdev-0.0.0.dev66.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
129
- omdev-0.0.0.dev66.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
130
- omdev-0.0.0.dev66.dist-info/RECORD,,
123
+ omdev-0.0.0.dev67.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
124
+ omdev-0.0.0.dev67.dist-info/METADATA,sha256=M8dV_C4AOWrLC0AwHO2mbcHF7x6USkIT-TJnGzGG4Y0,1252
125
+ omdev-0.0.0.dev67.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
126
+ omdev-0.0.0.dev67.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
127
+ omdev-0.0.0.dev67.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
128
+ omdev-0.0.0.dev67.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: setuptools (75.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
omdev/pycharm/__init__.py DELETED
File without changes
omdev/pycharm/runhack.py DELETED
@@ -1,1381 +0,0 @@
1
- """
2
- See:
3
- - https://github.com/JetBrains/intellij-community/blob/6400f70dde6f743e39a257a5a78cc51b644c835e/python/helpers/pycharm/_jb_pytest_runner.py
4
- - https://github.com/JetBrains/intellij-community/blob/5a4e584aa59767f2e7cf4bd377adfaaf7503984b/python/helpers/pycharm/_jb_runner_tools.py
5
- - https://github.com/JetBrains/intellij-community/blob/5a4e584aa59767f2e7cf4bd377adfaaf7503984b/python/helpers/pydev/_pydevd_bundle/pydevd_command_line_handling.py
6
- """ # noqa
7
- import os.path
8
- import sys
9
-
10
-
11
- ##
12
-
13
-
14
- class _cached_nullary: # noqa
15
- def __init__(self, fn):
16
- super().__init__()
17
-
18
- self._fn = fn
19
- self._value = self._missing = object()
20
-
21
- def __call__(self, *args, **kwargs): # noqa
22
- if self._value is self._missing:
23
- self._value = self._fn()
24
-
25
- return self._value
26
-
27
- def __get__(self, instance, owner): # noqa
28
- bound = instance.__dict__[self._fn.__name__] = self.__class__(self._fn.__get__(instance, owner))
29
- return bound
30
-
31
-
32
- def _attr_repr(obj, *atts):
33
- return f'{obj.__class__.__name__}({", ".join(f"{a}={getattr(obj, a)!r}" for a in atts)})'
34
-
35
-
36
- def _check_not_none(obj):
37
- if obj is None:
38
- raise RuntimeError
39
- return obj
40
-
41
-
42
- ##
43
-
44
-
45
- class AsDict:
46
- def as_dict(self): # type: () -> dict[str, object]
47
- raise TypeError
48
-
49
-
50
- class AsJson:
51
- def as_json(self): # type: () -> dict[str, object]
52
- raise TypeError
53
-
54
-
55
- ##
56
-
57
-
58
- class RunEnv(AsJson):
59
- def __init__(
60
- self,
61
- *,
62
- argv=None, # type: list[str] | None
63
- orig_argv=None, # type: list[str] | None
64
-
65
- cwd=None, # type: str | None
66
-
67
- library_roots=None, # type: list[str] | None
68
- path=None, # type: list[str] | None
69
- python_path=None, # type: list[str] | None
70
- ide_project_roots=None, # type: list[str] | None
71
- pycharm_hosted=None, # type: bool | None
72
-
73
- sys_path=None, # type: list[str] | None
74
- ) -> None:
75
- super().__init__()
76
-
77
- if argv is None:
78
- argv = sys.argv
79
- self._argv = list(argv)
80
-
81
- if orig_argv is None:
82
- orig_argv = sys.orig_argv
83
- self._orig_argv = list(orig_argv)
84
-
85
- if cwd is None:
86
- cwd = os.getcwd()
87
- self._cwd = cwd
88
-
89
- def get_env_path_list(k): # type: (str) -> list[str]
90
- v = os.environ.get(k, '')
91
- if v:
92
- return v.split(os.pathsep)
93
- else:
94
- return []
95
-
96
- if library_roots is None:
97
- library_roots = get_env_path_list('LIBRARY_ROOTS')
98
- self._library_roots = list(library_roots)
99
-
100
- if path is None:
101
- path = get_env_path_list('PATH')
102
- self._path = list(path)
103
-
104
- if python_path is None:
105
- python_path = get_env_path_list('PYTHONPATH')
106
- self._python_path = list(python_path)
107
-
108
- if ide_project_roots is None:
109
- ide_project_roots = get_env_path_list('IDE_PROJECT_ROOTS')
110
- self._ide_project_roots = list(ide_project_roots)
111
-
112
- if pycharm_hosted is None:
113
- pycharm_hosted = 'PYCHARM_HOSTED' in os.environ
114
- self._pycharm_hosted = pycharm_hosted
115
-
116
- if sys_path is None:
117
- sys_path = list(sys.path)
118
- self._sys_path = sys_path
119
-
120
- _SPEC_ATTRS = (
121
- 'argv',
122
- 'orig_argv',
123
-
124
- 'cwd',
125
-
126
- 'library_roots',
127
- 'path',
128
- 'python_path',
129
- 'ide_project_roots',
130
- 'pycharm_hosted',
131
-
132
- 'sys_path',
133
- )
134
-
135
- def as_json(self): # type: () -> dict[str, object]
136
- return {a: getattr(self, a) for a in self._SPEC_ATTRS}
137
-
138
- def __repr__(self) -> str:
139
- return _attr_repr(self, *self._SPEC_ATTRS)
140
-
141
- @property
142
- def argv(self): # type: () -> list[str]
143
- return self._argv
144
-
145
- @property
146
- def orig_argv(self): # type: () -> list[str]
147
- return self._orig_argv
148
-
149
- @property
150
- def cwd(self): # type: () -> str
151
- return self._cwd
152
-
153
- @property
154
- def library_roots(self): # type: () -> list[str]
155
- return self._library_roots
156
-
157
- @property
158
- def path(self): # type: () -> list[str]
159
- return self._path
160
-
161
- @property
162
- def python_path(self): # type: () -> list[str]
163
- return self._python_path
164
-
165
- @property
166
- def ide_project_roots(self): # type: () -> list[str]
167
- return self._ide_project_roots
168
-
169
- @property
170
- def pycharm_hosted(self): # type: () -> bool
171
- return self._pycharm_hosted
172
-
173
- @property
174
- def sys_path(self): # type: () -> list[str]
175
- return self._sys_path
176
-
177
-
178
- ##
179
-
180
-
181
- class Param:
182
- def __init__(
183
- self,
184
- name: str,
185
- cls, # type: type[Arg]
186
- ) -> None:
187
- super().__init__()
188
-
189
- if not issubclass(cls, Arg):
190
- raise TypeError(cls)
191
-
192
- self._name = name
193
- self._cls = cls
194
-
195
- @property
196
- def name(self) -> str:
197
- return self._name
198
-
199
- @property
200
- def cls(self): # type: () -> type[Arg]
201
- return self._cls
202
-
203
- def __repr__(self) -> str:
204
- return _attr_repr(self, 'name', 'cls')
205
-
206
-
207
- class Params:
208
- def __init__(
209
- self,
210
- params, # type: list[Param]
211
- ) -> None:
212
- super().__init__()
213
-
214
- self._params = params
215
- self._params_by_name = {} # type: dict[str, Param]
216
-
217
- for p in params:
218
- if p.name in self._params_by_name:
219
- raise KeyError(p.name)
220
- self._params_by_name[p.name] = p
221
-
222
- @property
223
- def params(self): # type: () -> list[Param]
224
- return self._params
225
-
226
- @property
227
- def params_by_name(self): # type: () -> dict[str, Param]
228
- return self._params_by_name
229
-
230
- def __repr__(self) -> str:
231
- return _attr_repr(self, 'params')
232
-
233
-
234
- #
235
-
236
-
237
- class Arg(AsJson):
238
- def __init__(self, param: Param) -> None:
239
- super().__init__()
240
-
241
- if not isinstance(param, Param):
242
- raise TypeError(param)
243
-
244
- self._param = param
245
-
246
- @property
247
- def param(self) -> Param:
248
- return self._param
249
-
250
-
251
- class BoolArg(Arg):
252
- def __repr__(self) -> str:
253
- return _attr_repr(self, 'param')
254
-
255
- def as_json(self): # type: () -> dict[str, object]
256
- return {self._param.name: True}
257
-
258
-
259
- class StrArg(Arg):
260
- def __init__(self, param: Param, value: str) -> None:
261
- super().__init__(param)
262
-
263
- self._value = value
264
-
265
- @property
266
- def value(self) -> str:
267
- return self._value
268
-
269
- def __repr__(self) -> str:
270
- return _attr_repr(self, 'param', 'value')
271
-
272
- def as_json(self): # type: () -> dict[str, object]
273
- return {self._param.name: self._value}
274
-
275
-
276
- class OptStrArg(Arg):
277
- def __init__(
278
- self,
279
- param: Param,
280
- value, # type: str | None
281
- ) -> None:
282
- super().__init__(param)
283
-
284
- self._value = value
285
-
286
- @property
287
- def value(self): # type: () -> str | None
288
- return self._value
289
-
290
- def __repr__(self) -> str:
291
- return _attr_repr(self, 'param', 'value')
292
-
293
- def as_json(self): # type: () -> dict[str, object]
294
- return {self._param.name: self._value}
295
-
296
-
297
- class FinalArg(Arg):
298
- def __init__(
299
- self,
300
- param: Param,
301
- values, # type: list[str]
302
- ) -> None:
303
- super().__init__(param)
304
-
305
- self._values = values
306
-
307
- @property
308
- def values(self): # type: () -> list[str]
309
- return self._values
310
-
311
- def __repr__(self) -> str:
312
- return _attr_repr(self, 'param', 'values')
313
-
314
- def as_json(self): # type: () -> dict[str, object]
315
- return {self._param.name: self._values}
316
-
317
-
318
- class Args(AsJson):
319
- def __init__(
320
- self,
321
- params: Params,
322
- args, # type: list[Arg]
323
- ) -> None:
324
- super().__init__()
325
-
326
- self._params = params
327
- self._args = args
328
-
329
- self._arg_lists_by_name = {} # type: dict[str, list[Arg]]
330
- for a in args:
331
- self._arg_lists_by_name.setdefault(a.param.name, []).append(a)
332
-
333
- @property
334
- def params(self): # type: () -> Params
335
- return self._params
336
-
337
- @property
338
- def args(self): # type: () -> list[Arg]
339
- return self._args
340
-
341
- @property
342
- def arg_lists_by_name(self): # type: () -> dict[str, list[Arg]]
343
- return self._arg_lists_by_name
344
-
345
- def without(self, *names: str) -> 'Args':
346
- return Args(
347
- self._params,
348
- [a for a in self._args if a.param.name not in names],
349
- )
350
-
351
- def __repr__(self) -> str:
352
- return _attr_repr(self, 'args')
353
-
354
- def as_json(self): # type: () -> dict[str, object]
355
- return {k: v for a in self._args for k, v in a.as_json().items()}
356
-
357
-
358
- #
359
-
360
-
361
- class ArgParseError(Exception):
362
- pass
363
-
364
-
365
- def parse_args(
366
- params: Params,
367
- argv, # type: list[str]
368
- ) -> Args:
369
- l = [] # type: list[Arg]
370
-
371
- it = iter(argv)
372
- for s in it:
373
- if len(s) > 1 and s.startswith('--'):
374
- s = s[2:]
375
- else:
376
- raise ArgParseError(s, argv)
377
-
378
- if '=' in s:
379
- k, _, v = s.partition('=')
380
- else:
381
- k, v = s, None
382
-
383
- p = params.params_by_name[k]
384
-
385
- if p.cls is BoolArg:
386
- if v is not None:
387
- raise ArgParseError(s, argv)
388
- l.append(BoolArg(p))
389
-
390
- elif p.cls is StrArg:
391
- if v is None:
392
- try:
393
- v = next(it)
394
- except StopIteration:
395
- raise ArgParseError(s, argv) # noqa
396
- l.append(StrArg(p, v))
397
-
398
- elif p.cls is OptStrArg:
399
- l.append(OptStrArg(p, v))
400
-
401
- elif p.cls is FinalArg:
402
- vs = [] # type: list[str]
403
- if v is not None:
404
- vs.append(v)
405
- vs.extend(it)
406
- l.append(FinalArg(p, vs))
407
-
408
- else:
409
- raise TypeError(p.cls)
410
-
411
- return Args(params, l)
412
-
413
-
414
- #
415
-
416
-
417
- def render_arg(arg): # type: (Arg) -> list[str]
418
- if isinstance(arg, BoolArg):
419
- return [f'--{arg.param.name}']
420
-
421
- elif isinstance(arg, StrArg):
422
- return [f'--{arg.param.name}', arg.value]
423
-
424
- elif isinstance(arg, OptStrArg):
425
- if arg.value is None:
426
- return [f'--{arg.param.name}']
427
- else:
428
- return [f'--{arg.param.name}={arg.value}']
429
-
430
- elif isinstance(arg, FinalArg):
431
- return [f'--{arg.param.name}', *arg.values] # noqa
432
-
433
- else:
434
- raise TypeError(arg)
435
-
436
-
437
- def render_args(args): # type: (list[Arg]) -> list[str]
438
- return [ra for a in args for ra in render_arg(a)]
439
-
440
-
441
- ##
442
-
443
-
444
- class Target(AsDict, AsJson):
445
- pass
446
-
447
-
448
- #
449
-
450
-
451
- class UserTarget(Target):
452
- def __init__(
453
- self,
454
- argv, # type: list[str]
455
- ) -> None:
456
- super().__init__()
457
-
458
- self._argv = argv
459
-
460
- @property
461
- def argv(self): # type: () -> list[str]
462
- return self._argv
463
-
464
-
465
- class FileTarget(UserTarget):
466
- def __init__(
467
- self,
468
- file: str,
469
- argv, # type: list[str]
470
- ) -> None:
471
- super().__init__(argv)
472
-
473
- self._file = file
474
-
475
- @property
476
- def file(self) -> str:
477
- return self._file
478
-
479
- def __repr__(self) -> str:
480
- return _attr_repr(self, 'file', 'argv')
481
-
482
- def as_dict(self): # type: () -> dict[str, object]
483
- return {
484
- 'file': self._file,
485
- 'argv': self._argv,
486
- }
487
-
488
- def as_json(self): # type: () -> dict[str, object]
489
- return self.as_dict()
490
-
491
-
492
- class ModuleTarget(UserTarget):
493
- def __init__(
494
- self,
495
- module: str,
496
- argv, # type: list[str]
497
- ) -> None:
498
- super().__init__(argv)
499
-
500
- self._module = module
501
-
502
- @property
503
- def module(self) -> str:
504
- return self._module
505
-
506
- def __repr__(self) -> str:
507
- return _attr_repr(self, 'module', 'argv')
508
-
509
- def as_dict(self): # type: () -> dict[str, object]
510
- return {
511
- 'module': self._module,
512
- 'argv': self._argv,
513
- }
514
-
515
- def as_json(self): # type: () -> dict[str, object]
516
- return self.as_dict()
517
-
518
-
519
- #
520
-
521
-
522
- class PycharmTarget(Target):
523
- def __init__(self, file: str, args: Args) -> None:
524
- super().__init__()
525
-
526
- if not isinstance(file, str):
527
- raise TypeError(file)
528
- self._file = file
529
-
530
- if not isinstance(args, Args):
531
- raise TypeError(args)
532
- self._args = args
533
-
534
- @property
535
- def file(self) -> str:
536
- return self._file
537
-
538
- @property
539
- def args(self) -> Args:
540
- return self._args
541
-
542
-
543
- class DebuggerTarget(PycharmTarget):
544
- def __init__(self, file: str, args: Args, target: Target) -> None:
545
- super().__init__(file, args)
546
-
547
- if isinstance(target, DebuggerTarget):
548
- raise TypeError(target)
549
- self._target = target
550
-
551
- @property
552
- def target(self) -> Target:
553
- return self._target
554
-
555
- def __repr__(self) -> str:
556
- return _attr_repr(self, 'file', 'args', 'target')
557
-
558
- def as_dict(self): # type: () -> dict[str, object]
559
- return {
560
- 'file': self._file,
561
- 'args': self._args,
562
- 'target': self._target,
563
- }
564
-
565
- def as_json(self): # type: () -> dict[str, object]
566
- return {
567
- 'debugger': self._file,
568
- 'args': self._args.as_json(),
569
- 'target': self._target.as_json(),
570
- }
571
-
572
-
573
- class Test(AsJson):
574
- def __init__(self, s: str) -> None:
575
- super().__init__()
576
-
577
- if not isinstance(s, str):
578
- raise TypeError(s)
579
-
580
- self._s = s
581
-
582
- @property
583
- def s(self) -> str:
584
- return self._s
585
-
586
- def __repr__(self) -> str:
587
- return _attr_repr(self, 's')
588
-
589
-
590
- class PathTest(Test):
591
- def as_json(self): # type: () -> dict[str, object]
592
- return {'path': self._s}
593
-
594
-
595
- class TargetTest(Test):
596
- def as_json(self): # type: () -> dict[str, object]
597
- return {'target': self._s}
598
-
599
-
600
- class TestRunnerTarget(PycharmTarget):
601
- def __init__(
602
- self,
603
- file: str,
604
- args: Args,
605
- tests, # type: list[Test]
606
- ) -> None:
607
- super().__init__(file, args)
608
-
609
- self._tests = tests
610
-
611
- @property
612
- def tests(self): # type: () -> list[Test]
613
- return self._tests
614
-
615
- def __repr__(self) -> str:
616
- return _attr_repr(self, 'file', 'args', 'tests')
617
-
618
- def as_dict(self): # type: () -> dict[str, object]
619
- return {
620
- 'file': self._file,
621
- 'args': self._args,
622
- 'tests': self._tests,
623
- }
624
-
625
- def as_json(self): # type: () -> dict[str, object]
626
- return {
627
- 'test_runner': self._file,
628
- 'args': self._args.as_json(),
629
- 'tests': [t.as_json() for t in self._tests],
630
- }
631
-
632
-
633
- #
634
-
635
-
636
- def is_pycharm_dir(s: str) -> bool:
637
- s = os.path.abspath(s)
638
- if not os.path.isdir(s):
639
- return False
640
-
641
- ps = s.split(os.sep)
642
-
643
- plat = getattr(sys, 'platform')
644
- if plat == 'darwin':
645
- # /Applications/PyCharm.app/Contents/bin/pycharm.vmoptions
646
- return ps[-1] == 'Contents' and os.path.isfile(os.path.join(s, 'bin', 'pycharm.vmoptions'))
647
-
648
- if plat == 'linux':
649
- # /snap/pycharm-professional/current/bin/pycharm64.vmoptions
650
- return os.path.isfile(os.path.join(s, 'bin', 'pycharm64.vmoptions'))
651
-
652
- return False
653
-
654
-
655
- def is_pycharm_file(given: str, expected: str) -> bool:
656
- dgs = os.path.abspath(given).split(os.sep)
657
- des = expected.split(os.sep)
658
- return (
659
- len(des) < len(dgs) and
660
- dgs[-len(des):] == des and
661
- is_pycharm_dir(os.sep.join(dgs[:-len(des)]))
662
- )
663
-
664
-
665
- class PycharmEntrypoint:
666
- def __init__(self, file: str, params: Params) -> None:
667
- super().__init__()
668
-
669
- self._file = file
670
- self._params = params
671
-
672
- @property
673
- def file(self) -> str:
674
- return self._file
675
-
676
- @property
677
- def params(self) -> Params:
678
- return self._params
679
-
680
- def __repr__(self) -> str:
681
- return _attr_repr(self, 'file', 'params')
682
-
683
-
684
- DEBUGGER_ENTRYPOINT = PycharmEntrypoint(
685
- 'plugins/python-ce/helpers/pydev/pydevd.py',
686
- Params([
687
- Param('port', StrArg),
688
- Param('vm_type', StrArg),
689
- Param('client', StrArg),
690
-
691
- Param('qt-support', OptStrArg),
692
-
693
- Param('file', FinalArg),
694
-
695
- Param('server', BoolArg),
696
- Param('DEBUG_RECORD_SOCKET_READS', BoolArg),
697
- Param('multiproc', BoolArg),
698
- Param('multiprocess', BoolArg),
699
- Param('save-signatures', BoolArg),
700
- Param('save-threading', BoolArg),
701
- Param('save-asyncio', BoolArg),
702
- Param('print-in-debugger-startup', BoolArg),
703
- Param('cmd-line', BoolArg),
704
- Param('module', BoolArg),
705
- Param('help', BoolArg),
706
- Param('DEBUG', BoolArg),
707
- ]),
708
- )
709
-
710
-
711
- TEST_RUNNER_ENTRYPOINT = PycharmEntrypoint(
712
- 'plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py',
713
- Params([
714
- Param('path', StrArg),
715
- Param('offset', StrArg),
716
- Param('target', StrArg),
717
-
718
- Param('', FinalArg),
719
- ]),
720
- )
721
-
722
-
723
- def try_parse_entrypoint_args(ep, argv): # type: (PycharmEntrypoint, list[str]) -> Args | None
724
- if not argv:
725
- return None
726
-
727
- if not is_pycharm_file(argv[0], ep.file):
728
- return None
729
-
730
- return parse_args(ep.params, argv[1:])
731
-
732
-
733
- def _make_module_target(
734
- argv, # type: list[str]
735
- ) -> ModuleTarget:
736
- if argv[0] == '-m':
737
- return ModuleTarget(argv[1], argv[2:])
738
- elif argv[0].startswith('-m'):
739
- return ModuleTarget(argv[0][2:], argv[1:])
740
- else:
741
- raise ArgParseError(argv)
742
-
743
-
744
- def parse_args_target(
745
- argv, # type: list[str]
746
- ) -> Target:
747
- if not argv:
748
- raise Exception
749
-
750
- elif (pa := try_parse_entrypoint_args(DEBUGGER_ENTRYPOINT, argv)) is not None:
751
- fa = pa.args[-1]
752
- if not isinstance(fa, FinalArg) or fa.param.name != 'file':
753
- raise TypeError(fa)
754
-
755
- st = parse_args_target(fa.values)
756
-
757
- if isinstance(st, TestRunnerTarget):
758
- if 'module' in pa.arg_lists_by_name:
759
- raise ArgParseError(argv)
760
-
761
- elif isinstance(st, FileTarget):
762
- if 'module' in pa.arg_lists_by_name:
763
- st = ModuleTarget(st.file, st.argv)
764
-
765
- else:
766
- raise TypeError(st)
767
-
768
- return DebuggerTarget(
769
- argv[0],
770
- pa.without('file', 'module'),
771
- st,
772
- )
773
-
774
- elif (pa := try_parse_entrypoint_args(TEST_RUNNER_ENTRYPOINT, argv)) is not None:
775
- ts = [] # type: list[Test]
776
- for a in pa.args:
777
- if isinstance(a, StrArg):
778
- if a.param.name == 'path':
779
- ts.append(PathTest(a.value))
780
- elif a.param.name == 'target':
781
- ts.append(TargetTest(a.value))
782
-
783
- return TestRunnerTarget(
784
- argv[0],
785
- pa.without('path', 'target'),
786
- ts,
787
- )
788
-
789
- elif argv[0].startswith('-m'):
790
- return _make_module_target(argv)
791
-
792
- else:
793
- return FileTarget(argv[0], argv[1:])
794
-
795
-
796
- #
797
-
798
-
799
- def render_target_args(tgt): # type: (Target) -> list[str]
800
- if isinstance(tgt, FileTarget):
801
- return [tgt.file, *tgt.argv]
802
-
803
- elif isinstance(tgt, ModuleTarget):
804
- return ['-m', *tgt.argv]
805
-
806
- elif isinstance(tgt, DebuggerTarget):
807
- l = [
808
- tgt.file,
809
- *render_args(tgt.args.args),
810
- ]
811
- dt = tgt.target
812
- if isinstance(dt, ModuleTarget):
813
- l.extend(['--module', '--file', dt.module, *dt.argv])
814
- else:
815
- l.extend(['--file', *render_target_args(dt)])
816
- return l
817
-
818
- elif isinstance(tgt, TestRunnerTarget):
819
- l = [
820
- tgt.file,
821
- ]
822
- for t in tgt.tests:
823
- if isinstance(t, PathTest):
824
- l.extend(['--path', t.s])
825
- elif isinstance(t, TargetTest):
826
- l.extend(['--target', t.s])
827
- else:
828
- raise TypeError(t)
829
- l.extend(render_args(tgt.args.args))
830
- return l
831
-
832
- else:
833
- raise TypeError(tgt)
834
-
835
-
836
- ##
837
-
838
-
839
- class Exec(AsDict, AsJson):
840
- def __init__(
841
- self,
842
- exe: str,
843
- exe_args, # type: list[str]
844
- target: Target,
845
- ) -> None:
846
- super().__init__()
847
-
848
- self._exe = exe
849
- self._exe_args = exe_args
850
- self._target = target
851
-
852
- @property
853
- def exe(self) -> str:
854
- return self._exe
855
-
856
- @property
857
- def exe_args(self): # type: () -> list[str]
858
- return self._exe_args
859
-
860
- @property
861
- def target(self) -> Target:
862
- return self._target
863
-
864
- def __repr__(self) -> str:
865
- return _attr_repr(self, 'exe', 'exe_args', 'target')
866
-
867
- def as_dict(self): # type: () -> dict[str, object]
868
- return {
869
- 'exe': self._exe,
870
- 'exe_args': self._exe_args,
871
- 'target': self._target,
872
- }
873
-
874
- def as_json(self): # type: () -> dict[str, object]
875
- return {
876
- 'exe': self._exe,
877
- 'exe_args': self._exe_args,
878
- 'target': self._target.as_json(),
879
- }
880
-
881
-
882
- def parse_exec(
883
- exe_argv, # type: list[str]
884
- ) -> Exec:
885
- it = iter(exe_argv)
886
- exe = next(it)
887
-
888
- exe_args = [] # type: list[str]
889
-
890
- for a in it:
891
- if a.startswith('-X'):
892
- if a == '-X':
893
- exe_args.extend([a, next(it)])
894
- else:
895
- exe_args.append(a)
896
- else:
897
- break
898
- else:
899
- raise Exception(exe_argv)
900
-
901
- argv = [a, *it]
902
-
903
- if argv[0].startswith('-m'):
904
- tgt = _make_module_target(argv) # type: Target
905
-
906
- else:
907
- tgt = parse_args_target(argv)
908
-
909
- return Exec(
910
- exe,
911
- exe_args,
912
- tgt,
913
- )
914
-
915
-
916
- def render_exec_args(exe): # type: (Exec) -> list[str]
917
- l = [
918
- exe.exe,
919
- *exe.exe_args,
920
- ]
921
-
922
- et = exe.target
923
-
924
- if isinstance(et, ModuleTarget):
925
- l.extend(['-m', et.module, *et.argv])
926
-
927
- else:
928
- l.extend(render_target_args(et))
929
-
930
- return l
931
-
932
-
933
- ##
934
-
935
-
936
- class ExecDecision(AsJson):
937
- def __init__(
938
- self,
939
- target: Target,
940
- *,
941
- cwd=None, # type: str | None
942
- python_path=None, # type: list[str] | None
943
- sys_path=None, # type: list[str] | None
944
- os_exec: bool = False,
945
- ) -> None:
946
- super().__init__()
947
-
948
- if not isinstance(target, Target):
949
- raise TypeError(Target)
950
- self._target = target
951
-
952
- self._cwd = cwd
953
- self._python_path = python_path
954
- self._sys_path = sys_path
955
- self._os_exec = os_exec
956
-
957
- @property
958
- def target(self) -> Target:
959
- return self._target
960
-
961
- @property
962
- def cwd(self): # type: () -> str | None
963
- return self._cwd
964
-
965
- @property
966
- def python_path(self): # type: () -> list[str] | None
967
- return self._python_path
968
-
969
- @property
970
- def sys_path(self): # type: () -> list[str] | None
971
- return self._sys_path
972
-
973
- @property
974
- def os_exec(self) -> bool:
975
- return self._os_exec
976
-
977
- def __repr__(self) -> str:
978
- return _attr_repr(
979
- self,
980
- 'target',
981
- 'cwd',
982
- 'python_path',
983
- 'sys_path',
984
- 'os_exec',
985
- )
986
-
987
- def as_json(self): # type: () -> dict[str, object]
988
- return {
989
- 'target': self._target.as_json(),
990
- 'cwd': self._cwd,
991
- 'python_path': self._python_path,
992
- 'sys_path': self._sys_path,
993
- 'os_exec': self._os_exec,
994
- }
995
-
996
-
997
- class ExecDecider:
998
- def __init__(
999
- self,
1000
- env: RunEnv,
1001
- exe: Exec,
1002
- root_dir: str,
1003
- *,
1004
- debug_fn=None,
1005
- ) -> None:
1006
- super().__init__()
1007
-
1008
- self._env = env
1009
- self._exe = exe
1010
- self._root_dir = root_dir
1011
-
1012
- self._debug_fn = debug_fn
1013
-
1014
- def _debug(self, arg):
1015
- if self._debug_fn is not None:
1016
- self._debug_fn(arg)
1017
-
1018
- def _filter_out_cwd(self, lst): # type: (list[str]) -> list[str]
1019
- return [p for p in lst if p != self._env.cwd]
1020
-
1021
- def _decide_file_target(self, tgt): # type: (Target) -> ExecDecision | None
1022
- if not isinstance(tgt, FileTarget):
1023
- return None
1024
-
1025
- new_file = os.path.abspath(tgt.file)
1026
-
1027
- return ExecDecision(
1028
- FileTarget(**{ # type: ignore
1029
- **tgt.as_dict(),
1030
- 'file': new_file,
1031
- }),
1032
- cwd=self._root_dir,
1033
- )
1034
-
1035
- def _decide_module_target_not_in_root(self, tgt): # type: (Target) -> ExecDecision | None
1036
- if not (isinstance(tgt, ModuleTarget) and self._env.cwd != self._root_dir):
1037
- return None
1038
-
1039
- rel_path = os.path.relpath(self._env.cwd, self._root_dir)
1040
- new_mod = '.'.join([rel_path.replace(os.sep, '.'), tgt.module]) # noqa
1041
-
1042
- return ExecDecision(
1043
- ModuleTarget(**{ # type: ignore
1044
- **tgt.as_dict(),
1045
- 'module': new_mod,
1046
- }),
1047
- cwd=self._root_dir,
1048
- os_exec=True,
1049
- )
1050
-
1051
- def _decide_debugger_file_target(self, tgt): # type: (Target) -> ExecDecision | None
1052
- if not isinstance(tgt, DebuggerTarget):
1053
- return None
1054
-
1055
- dt = tgt.target
1056
- if not (isinstance(dt, FileTarget) and dt.file.endswith('.py')):
1057
- return None
1058
-
1059
- af = os.path.abspath(dt.file)
1060
- rp = os.path.relpath(af, self._root_dir).split(os.path.sep)
1061
- mod = '.'.join([*rp[:-1], rp[-1][:-3]])
1062
- new_dt = ModuleTarget(
1063
- mod,
1064
- dt.argv,
1065
- )
1066
-
1067
- return ExecDecision(
1068
- DebuggerTarget(**{ # type: ignore
1069
- **tgt.as_dict(),
1070
- 'target': new_dt,
1071
- }),
1072
- cwd=self._root_dir,
1073
- python_path=self._filter_out_cwd(self._env.python_path),
1074
- sys_path=self._filter_out_cwd(self._env.sys_path),
1075
- )
1076
-
1077
- def _decide_debugger_module_target_not_in_root(self, tgt): # type: (Target) -> ExecDecision | None
1078
- if not (isinstance(tgt, DebuggerTarget) and self._env.cwd != self._root_dir):
1079
- return None
1080
-
1081
- dt = tgt.target
1082
- if not isinstance(dt, ModuleTarget):
1083
- return None
1084
-
1085
- rp = os.path.relpath(self._env.cwd, self._root_dir).split(os.path.sep)
1086
- mod = '.'.join([*rp, dt.module])
1087
- new_dt = ModuleTarget(
1088
- mod,
1089
- dt.argv,
1090
- )
1091
-
1092
- return ExecDecision(
1093
- DebuggerTarget(**{ # type: ignore
1094
- **tgt.as_dict(),
1095
- 'target': new_dt,
1096
- }),
1097
- cwd=self._root_dir,
1098
- python_path=self._filter_out_cwd(self._env.python_path),
1099
- sys_path=self._filter_out_cwd(self._env.sys_path),
1100
- )
1101
-
1102
- def _decide_debugger_test_runner_target_not_in_root(self, tgt): # type: (Target) -> ExecDecision | None
1103
- if not (isinstance(tgt, DebuggerTarget) and self._env.cwd != self._root_dir):
1104
- return None
1105
-
1106
- dt = tgt.target
1107
- if not isinstance(dt, TestRunnerTarget):
1108
- return None
1109
-
1110
- def fix_test(t):
1111
- if isinstance(t, PathTest):
1112
- return PathTest(os.path.abspath(t.s))
1113
-
1114
- elif isinstance(t, TargetTest):
1115
- if ':' in t.s:
1116
- l, _, r = t.s.partition(':')
1117
- return TargetTest(':'.join([os.path.abspath(l), r]))
1118
- else:
1119
- return TargetTest(os.path.abspath(t.s))
1120
-
1121
- else:
1122
- raise TypeError(t)
1123
-
1124
- new_tests = [fix_test(t) for t in dt.tests]
1125
-
1126
- new_dt = TestRunnerTarget(**{ # type: ignore
1127
- **dt.as_dict(),
1128
- 'tests': new_tests,
1129
- })
1130
-
1131
- return ExecDecision(
1132
- DebuggerTarget(**{ # type: ignore
1133
- **tgt.as_dict(),
1134
- 'target': new_dt,
1135
- }),
1136
- cwd=self._root_dir,
1137
- python_path=self._filter_out_cwd(self._env.python_path),
1138
- sys_path=self._filter_out_cwd(self._env.sys_path),
1139
- )
1140
-
1141
- def decide(self, tgt): # type: (Target) -> ExecDecision | None
1142
- for fn in [
1143
- self._decide_file_target,
1144
- self._decide_module_target_not_in_root,
1145
- self._decide_debugger_file_target,
1146
- self._decide_debugger_module_target_not_in_root,
1147
- self._decide_debugger_test_runner_target_not_in_root,
1148
- ]:
1149
- if (ne := fn(tgt)) is not None:
1150
- self._debug(f'{fn.__name__=}')
1151
- return ne
1152
-
1153
- return None
1154
-
1155
-
1156
- ##
1157
-
1158
-
1159
- class HackRunner:
1160
- def __init__(
1161
- self,
1162
- *,
1163
- is_debug: bool = False,
1164
- is_enabled: bool = False,
1165
- ) -> None:
1166
- super().__init__()
1167
-
1168
- self._is_debug = is_debug
1169
- self._is_enabled = is_enabled
1170
-
1171
- def _debug(self, arg):
1172
- if not self._is_debug:
1173
- return
1174
-
1175
- if isinstance(arg, str):
1176
- s = arg
1177
- else:
1178
- try:
1179
- import pprint
1180
- except ImportError:
1181
- s = repr(arg)
1182
- else:
1183
- s = pprint.pformat(arg, sort_dicts=False)
1184
-
1185
- print(s, file=sys.stderr)
1186
-
1187
- @_cached_nullary
1188
- def _env(self) -> RunEnv:
1189
- return RunEnv()
1190
-
1191
- @_cached_nullary
1192
- def _root_dir(self): # type: () -> str | None
1193
- env = self._env()
1194
-
1195
- if env.ide_project_roots:
1196
- root_dir = os.path.abspath(env.ide_project_roots[0])
1197
- else:
1198
- root_dir = os.path.abspath(env.sys_path[0])
1199
-
1200
- self._debug(f'{root_dir=}')
1201
- if not os.path.isfile(os.path.join(root_dir, 'pyproject.toml')):
1202
- return None
1203
-
1204
- return root_dir
1205
-
1206
- @_cached_nullary
1207
- def _exe(self) -> Exec:
1208
- exe = parse_exec(self._env().orig_argv)
1209
- self._debug(exe.as_json())
1210
- return exe
1211
-
1212
- @_cached_nullary
1213
- def _decider(self) -> ExecDecider:
1214
- return ExecDecider(
1215
- self._env(),
1216
- self._exe(),
1217
- _check_not_none(self._root_dir()),
1218
- debug_fn=self._debug,
1219
- )
1220
-
1221
- def _apply(self, dec: ExecDecision) -> None:
1222
- if dec.cwd is not None:
1223
- os.chdir(dec.cwd)
1224
-
1225
- if dec.python_path is not None:
1226
- os.environ['PYTHONPATH'] = os.pathsep.join(dec.python_path)
1227
-
1228
- if dec.sys_path is not None:
1229
- sys.path = dec.sys_path
1230
-
1231
- if dec.os_exec:
1232
- new_exe = Exec(**{
1233
- **self._exe().as_dict(),
1234
- 'target': dec.target,
1235
- })
1236
-
1237
- reexec_argv = render_exec_args(new_exe)
1238
- self._debug(f'{reexec_argv=}')
1239
-
1240
- os.execvp(reexec_argv[0], reexec_argv)
1241
-
1242
- else:
1243
- new_argv = render_target_args(dec.target)
1244
- self._debug(new_argv)
1245
-
1246
- sys.argv = new_argv
1247
-
1248
- @_cached_nullary
1249
- def run(self) -> None:
1250
- # breakpoint()
1251
-
1252
- env = self._env()
1253
- self._debug(env.as_json())
1254
-
1255
- if not self._is_enabled:
1256
- return
1257
-
1258
- if not env.pycharm_hosted:
1259
- return
1260
-
1261
- exe = self._exe()
1262
- dec = self._decider().decide(exe.target)
1263
- if dec is None:
1264
- return
1265
-
1266
- self._debug(dec.as_json())
1267
- self._apply(dec)
1268
-
1269
-
1270
- ##
1271
-
1272
-
1273
- ENABLED_ENV_VAR = 'OMLISH_PYCHARM_RUNHACK_ENABLED'
1274
- DEBUG_ENV_VAR = 'OMLISH_PYCHARM_RUNHACK_DEBUG'
1275
-
1276
- _DEFAULT_DEBUG = False
1277
- _DEFAULT_ENABLED = True
1278
-
1279
-
1280
- #
1281
-
1282
-
1283
- _HAS_RUN = False
1284
-
1285
-
1286
- _BOOL_ENV_VAR_VALUES = {
1287
- s: b
1288
- for b, ss in [
1289
- (True, ['1', 'true', 't']),
1290
- (False, ['0', 'false', 'f']),
1291
- ]
1292
- for s in ss
1293
- }
1294
-
1295
-
1296
- def _get_opt_env_bool(n, d): # type: (str | None, bool) -> bool
1297
- if n is None or n not in os.environ:
1298
- return d
1299
- return _BOOL_ENV_VAR_VALUES[os.environ[n]]
1300
-
1301
-
1302
- def _run() -> None:
1303
- global _HAS_RUN
1304
- if _HAS_RUN:
1305
- return
1306
- _HAS_RUN = True
1307
-
1308
- runner = HackRunner(
1309
- is_debug=_get_opt_env_bool(DEBUG_ENV_VAR, _DEFAULT_DEBUG),
1310
- is_enabled=_get_opt_env_bool(ENABLED_ENV_VAR, _DEFAULT_ENABLED),
1311
- )
1312
-
1313
- runner.run()
1314
-
1315
-
1316
- ##
1317
-
1318
-
1319
- _DEFAULT_PTH_FILE_NAME = f'omlish-{"-".join(__package__.split(".")[1:])}-runhack.pth'
1320
- _DEFAULT_PTH_MODULE_NAME = __package__ + '.runhack'
1321
-
1322
-
1323
- def _build_pth_file_src(module_name: str) -> str:
1324
- return (
1325
- 'import sys; '
1326
- r"exec('\n'.join(["
1327
- "'try:', "
1328
- f"' import {module_name}', "
1329
- "'except ImportError:', "
1330
- "' pass', "
1331
- "'else:', "
1332
- f"' {module_name}._run()'"
1333
- "]))"
1334
- )
1335
-
1336
-
1337
- def _install_pth_file(
1338
- *,
1339
- file_name: str = _DEFAULT_PTH_FILE_NAME,
1340
- module_name: str = _DEFAULT_PTH_MODULE_NAME,
1341
- dry_run: bool = False,
1342
- ) -> None:
1343
- import site
1344
-
1345
- if os.path.isfile(file := os.path.join(site.getsitepackages()[0], file_name)):
1346
- return
1347
-
1348
- src = _build_pth_file_src(module_name)
1349
-
1350
- if dry_run:
1351
- print(file)
1352
- print()
1353
- print(src)
1354
-
1355
- else:
1356
- with open(file, 'w') as f:
1357
- f.write(src)
1358
-
1359
-
1360
- if __name__ == '__main__':
1361
- def _main() -> None:
1362
- import argparse
1363
-
1364
- parser = argparse.ArgumentParser()
1365
-
1366
- subparsers = parser.add_subparsers()
1367
-
1368
- def install_cmd(args):
1369
- _install_pth_file(dry_run=args.dry_run)
1370
-
1371
- parser_install = subparsers.add_parser('install')
1372
- parser_install.add_argument('--dry-run', action='store_true')
1373
- parser_install.set_defaults(func=install_cmd)
1374
-
1375
- args = parser.parse_args()
1376
- if not getattr(args, 'func', None):
1377
- parser.print_help()
1378
- else:
1379
- args.func(args)
1380
-
1381
- _main()