omdev 0.0.0.dev371__py3-none-any.whl → 0.0.0.dev373__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/.manifests.json CHANGED
@@ -71,6 +71,21 @@
71
71
  }
72
72
  }
73
73
  },
74
+ {
75
+ "module": ".intellij.cli",
76
+ "attr": "_CLI_MODULE",
77
+ "file": "omdev/intellij/cli.py",
78
+ "line": 81,
79
+ "value": {
80
+ "$.cli.types.CliModule": {
81
+ "cmd_name": [
82
+ "intellij",
83
+ "ij"
84
+ ],
85
+ "mod_name": "omdev.intellij.cli"
86
+ }
87
+ }
88
+ },
74
89
  {
75
90
  "module": ".interp.__main__",
76
91
  "attr": "_CLI_MODULE",
@@ -378,21 +393,6 @@
378
393
  }
379
394
  }
380
395
  },
381
- {
382
- "module": ".tools.intellij",
383
- "attr": "_CLI_MODULE",
384
- "file": "omdev/tools/intellij.py",
385
- "line": 248,
386
- "value": {
387
- "$.cli.types.CliModule": {
388
- "cmd_name": [
389
- "intellij",
390
- "ij"
391
- ],
392
- "mod_name": "omdev.tools.intellij"
393
- }
394
- }
395
- },
396
396
  {
397
397
  "module": ".tools.json.__main__",
398
398
  "attr": "_CLI_MODULE",
@@ -25,12 +25,12 @@ TODO (old):
25
25
  - version generators - one for ast
26
26
  - configurable serde - marshal vs pickle? marshal w/ override for ndarray to write to file?
27
27
  - ok:
28
- - @fn - version, passive=False, deps=[Objectable, ]
28
+ - @fn - version, passive=False, deps=[Objectable, ...]
29
29
  - it no version use ast - specifically {'ast': <md5>}
30
30
  - but if present just use literal they gave, probably int
31
31
  - idiom: Version can be a frozendict, conventionally of str -> ta.Hashable
32
32
  - auto deps - fn can get containing Packages
33
- - Module, Resource,
33
+ - Module, Resource, ...
34
34
  - hrm.. LiteralVersion, MapVersion? + custom Marshal? need to deser as frozendict
35
35
  - storage
36
36
  - object table? w/ versions? strictly one row per object, evict objects with diff versions than those encountered
@@ -0,0 +1,11 @@
1
+ from .ides import ( # noqa
2
+ Ide,
3
+
4
+ infer_directory_ide,
5
+
6
+ get_ide_version,
7
+ )
8
+
9
+ from .open import ( # noqa
10
+ open_ide,
11
+ )
omdev/intellij/cli.py ADDED
@@ -0,0 +1,86 @@
1
+ """
2
+ TODO:
3
+ - PyCharm.app/Contents/plugins/python-ce/helpers/pydev/_pydevd_bundle/pydevd_constants.py -> USE_LOW_IMPACT_MONITORING
4
+ - DISPLAY=":1" - ls /tmp/.X11-unix/X1 ?
5
+ - https://unix.stackexchange.com/questions/17255/is-there-a-command-to-list-all-open-displays-on-a-machine
6
+ - w -oush
7
+ """
8
+ import inspect
9
+ import os.path
10
+ import subprocess
11
+ import tempfile
12
+
13
+ from omlish.argparse import all as ap
14
+
15
+ from ..cli import CliModule
16
+ from .ides import Ide
17
+ from .ides import get_ide_version
18
+ from .ides import infer_directory_ide
19
+ from .open import open_ide
20
+
21
+
22
+ ##
23
+
24
+
25
+ class IntellijCli(ap.Cli):
26
+ @ap.cmd(
27
+ ap.arg('python-exe'),
28
+ ap.arg('args', nargs=ap.REMAINDER),
29
+ )
30
+ def pycharm_runhack(self) -> int:
31
+ if not os.path.isfile(exe := self.args.python_exe):
32
+ raise FileNotFoundError(exe)
33
+
34
+ from omlish.diag._pycharm import runhack # noqa
35
+ src = inspect.getsource(runhack)
36
+
37
+ src_file = tempfile.mktemp(__package__ + '-runhack') # noqa
38
+ with open(src_file, 'w') as f:
39
+ f.write(src)
40
+
41
+ proc = subprocess.run([exe, src_file, *self.args.args], check=False)
42
+ return proc.returncode
43
+
44
+ #
45
+
46
+ def _get_ide(
47
+ self,
48
+ *,
49
+ cwd: str | None = None,
50
+ ) -> Ide: # noqa
51
+ if (ai := self.args.ide) is not None:
52
+ return Ide[ai.upper()] # noqa
53
+
54
+ if (ii := infer_directory_ide(cwd)) is not None:
55
+ return ii
56
+
57
+ return Ide.PYCHARM
58
+
59
+ @ap.cmd(
60
+ ap.arg('-e', '--ide'),
61
+ )
62
+ def version(self) -> None:
63
+ i = self._get_ide()
64
+ v = get_ide_version(i)
65
+ print(v)
66
+
67
+ @ap.cmd(
68
+ ap.arg('dir', nargs='?'),
69
+ ap.arg('-e', '--ide'),
70
+ )
71
+ def open(self) -> None:
72
+ dir = os.path.abspath(self.args.dir or '.') # noqa
73
+ ide = self._get_ide(cwd=dir)
74
+ open_ide(dir, ide=ide)
75
+
76
+
77
+ def _main() -> None:
78
+ IntellijCli()(exit=True)
79
+
80
+
81
+ # @omlish-manifest
82
+ _CLI_MODULE = CliModule(['intellij', 'ij'], __name__)
83
+
84
+
85
+ if __name__ == '__main__':
86
+ _main()
omdev/intellij/ides.py ADDED
@@ -0,0 +1,85 @@
1
+ """
2
+ TODO:
3
+ - read .idea/ for inference?
4
+ """
5
+ import enum
6
+ import os.path
7
+ import typing as ta
8
+
9
+ from omlish.diag.pycharm import get_pycharm_version
10
+
11
+
12
+ ##
13
+
14
+
15
+ class Ide(enum.Enum):
16
+ PYCHARM = enum.auto()
17
+ IDEA = enum.auto()
18
+ CLION = enum.auto()
19
+ WEBSTORM = enum.auto()
20
+ GOLAND = enum.auto()
21
+
22
+
23
+ ##
24
+
25
+
26
+ _INFER_FILE_NAME_SETS_BY_IDE: ta.Mapping[Ide, ta.AbstractSet[str]] = {
27
+ Ide.PYCHARM: frozenset([
28
+ 'setup.py',
29
+ 'setup.cfg',
30
+ 'pyproject.toml',
31
+ 'requirements.txt',
32
+ 'Pipfile',
33
+ 'poetry.lock',
34
+ '.python-version',
35
+ 'wsgi.py',
36
+ 'asgi.py',
37
+ 'manage.py',
38
+ ]),
39
+ Ide.IDEA: frozenset([
40
+ 'pom.xml',
41
+ 'mvnw',
42
+ 'build.gradle',
43
+ 'build.gradle.kts',
44
+ 'gradlew',
45
+ 'module-info.java',
46
+ '.java-version',
47
+ ]),
48
+ Ide.CLION: frozenset([
49
+ 'CMakeLists.txt',
50
+ 'configure.ac',
51
+ 'configure.in',
52
+ 'config.h.in',
53
+ 'vcpkg.json',
54
+ ]),
55
+ Ide.WEBSTORM: frozenset([
56
+ 'package.json',
57
+ 'package-lock.json',
58
+ ]),
59
+ Ide.GOLAND: frozenset([
60
+ 'go.mod',
61
+ 'go.sum',
62
+ ]),
63
+ }
64
+
65
+
66
+ def infer_directory_ide(cwd: str | None) -> Ide | None:
67
+ if cwd is None:
68
+ cwd = os.getcwd()
69
+
70
+ for i, fs in _INFER_FILE_NAME_SETS_BY_IDE.items():
71
+ for f in fs:
72
+ if os.path.exists(os.path.join(cwd, f)):
73
+ return i
74
+
75
+ return None
76
+
77
+
78
+ ##
79
+
80
+
81
+ def get_ide_version(ide: Ide) -> str:
82
+ if ide is Ide.PYCHARM:
83
+ return get_pycharm_version()
84
+ else:
85
+ raise ValueError(ide)
omdev/intellij/open.py ADDED
@@ -0,0 +1,113 @@
1
+ import dataclasses as dc
2
+ import os.path
3
+ import shlex
4
+ import subprocess
5
+ import sys
6
+ import tempfile
7
+ import typing as ta
8
+
9
+ from .ides import Ide
10
+ from .ides import infer_directory_ide
11
+
12
+
13
+ ##
14
+
15
+
16
+ _DARWIN_OPEN_SCRIPT_APP_BY_IDE: ta.Mapping[Ide, str] = {
17
+ Ide.PYCHARM: 'PyCharm',
18
+ Ide.CLION: 'CLion',
19
+ Ide.IDEA: 'IntelliJ IDEA',
20
+ Ide.WEBSTORM: 'WebStorm',
21
+ Ide.GOLAND: 'GoLand',
22
+ }
23
+
24
+ _DARWIN_OPEN_SCRIPT = """
25
+ tell application "{app}"
26
+ activate
27
+ open "{dir}"
28
+ end tell
29
+ return
30
+ """
31
+
32
+
33
+ #
34
+
35
+
36
+ _LINUX_WM_CLASS_BY_IDE: ta.Mapping[Ide, str] = {
37
+ Ide.PYCHARM: 'jetbrains-pycharm.jetbrains-pycharm',
38
+ Ide.CLION: 'jetbrains-clion.jetbrains-clion',
39
+ Ide.IDEA: 'jetbrains-idea.jetbrains-idea',
40
+ Ide.WEBSTORM: 'jetbrains-webstorm.jetbrains-webstorm',
41
+ Ide.GOLAND: 'jetbrains-goland.jetbrains-goland',
42
+ }
43
+
44
+
45
+ # sudo apt install xdotool wmctrl
46
+ # TODO: nohup pycharm.sh "$PROJECT_PATH" > /dev/null 2>&1 & ?
47
+ _LINUX_OPEN_SCRIPT = """
48
+ xdotool key --delay 1000 alt+f alt+o
49
+ xdotool type --delay 1000 {dir}
50
+ xdotool key Return
51
+ """
52
+
53
+
54
+ @dc.dataclass(frozen=True)
55
+ class _WmctrlLine:
56
+ window_id: str
57
+ desktop_id: str
58
+ pid: str
59
+ wm_class: str
60
+ user: str
61
+ title: str
62
+
63
+
64
+ # TODO: https://stackoverflow.com/a/79016360
65
+ def _parse_wmctrl_lxp_line(l: str) -> _WmctrlLine:
66
+ return _WmctrlLine(*l.strip().split(maxsplit=5))
67
+
68
+
69
+ #
70
+
71
+
72
+ def open_ide(
73
+ dir: str, # noqa
74
+ *,
75
+ ide: Ide | None = None,
76
+ default_ide: Ide = Ide.PYCHARM,
77
+ ) -> None:
78
+ if ide is None:
79
+ if (ide := infer_directory_ide(dir)) is None:
80
+ ide = default_ide
81
+
82
+ if (plat := sys.platform) == 'darwin':
83
+ if '"' in dir:
84
+ raise ValueError(dir)
85
+
86
+ scpt_src = _DARWIN_OPEN_SCRIPT.format(
87
+ app=_DARWIN_OPEN_SCRIPT_APP_BY_IDE[ide],
88
+ dir=dir,
89
+ )
90
+
91
+ scpt_file = tempfile.mktemp(__package__ + '-intellij-open') # noqa
92
+ with open(scpt_file, 'w') as f:
93
+ f.write(scpt_src)
94
+
95
+ subprocess.check_call(['osascript', scpt_file])
96
+
97
+ elif plat == 'linux':
98
+ env = os.environ
99
+ env.setdefault('DISPLAY', ':1')
100
+
101
+ wmc_out = subprocess.check_output(['wmctrl', '-lxp'], env=env).decode()
102
+ wls = [_parse_wmctrl_lxp_line(l) for l in wmc_out.splitlines()]
103
+ tgt_wmc = _LINUX_WM_CLASS_BY_IDE[ide]
104
+ tgt_wl = next(wl for wl in wls if wl.wm_class == tgt_wmc)
105
+ subprocess.check_call(['wmctrl', '-ia', tgt_wl.window_id], env=env)
106
+
107
+ scpt = _LINUX_OPEN_SCRIPT.format(dir=shlex.quote(os.path.expanduser(dir)))
108
+ print(scpt)
109
+ # subprocess.check_call(scpt, shell=True, env=env) # noqa
110
+ raise NotImplementedError
111
+
112
+ else:
113
+ raise OSError(plat)
omdev/precheck/imports.py CHANGED
@@ -66,11 +66,11 @@ class RootRelativeImportPrecheck(Precheck['RootRelativeImportPrecheck.Config']):
66
66
  if f.endswith('.py')
67
67
  ]
68
68
 
69
- for py_file in py_files:
69
+ for py_file in sorted(py_files):
70
70
  async for v in self._run_py_file(py_file, src_root):
71
71
  yield v
72
72
 
73
73
  async def run(self) -> ta.AsyncGenerator[Precheck.Violation]:
74
- for src_root in self._context.src_roots:
74
+ for src_root in sorted(self._context.src_roots):
75
75
  async for v in self._run_src_root(src_root):
76
76
  yield v
omdev/precheck/main.py CHANGED
@@ -40,6 +40,7 @@ from .imports import RootRelativeImportPrecheck
40
40
  from .lite import LitePython8Precheck
41
41
  from .manifests import ManifestsPrecheck
42
42
  from .scripts import ScriptDepsPrecheck
43
+ from .unicode import UnicodePrecheck
43
44
 
44
45
 
45
46
  log = logging.getLogger(__name__)
@@ -92,6 +93,7 @@ def _check_cmd(args) -> None:
92
93
  ManifestsPrecheck.Config(),
93
94
  RootRelativeImportPrecheck.Config(),
94
95
  ScriptDepsPrecheck.Config(),
96
+ UnicodePrecheck.Config(),
95
97
  ]
96
98
 
97
99
  with inj.create_managed_injector(bind_main(
@@ -21,7 +21,7 @@ class ManifestsPrecheck(Precheck['ManifestsPrecheck.Config']):
21
21
  self._context = context
22
22
 
23
23
  async def run(self) -> ta.AsyncGenerator[Precheck.Violation]:
24
- for src_root in self._context.src_roots:
24
+ for src_root in sorted(self._context.src_roots):
25
25
  try:
26
26
  MANIFEST_LOADER.load(src_root)
27
27
  except Exception as e: # noqa
omdev/precheck/scripts.py CHANGED
@@ -27,11 +27,11 @@ class ScriptDepsPrecheck(Precheck['ScriptDepsPrecheck.Config']):
27
27
  self._context = context
28
28
 
29
29
  async def run(self) -> ta.AsyncGenerator[Precheck.Violation]:
30
- for fp in magic.find_magic_files(
30
+ for fp in sorted(magic.find_magic_files(
31
31
  magic.PY_MAGIC_STYLE,
32
32
  self._context.src_roots,
33
33
  keys=['@omlish-script'],
34
- ):
34
+ )):
35
35
  if not (stat.S_IXUSR & os.stat(fp).st_mode):
36
36
  yield Precheck.Violation(self, f'script {fp} is not executable')
37
37
 
@@ -0,0 +1,81 @@
1
+ import dataclasses as dc
2
+ import os
3
+ import typing as ta
4
+ import unicodedata
5
+
6
+ from omlish.text.filecache import TextFileCache
7
+
8
+ from .base import Precheck
9
+ from .base import PrecheckContext
10
+ from .caches import DirWalkCache
11
+ from .caches import HeadersCache
12
+
13
+
14
+ ##
15
+
16
+
17
+ class UnicodePrecheck(Precheck['UnicodePrecheck.Config']):
18
+ @dc.dataclass(frozen=True)
19
+ class Config(Precheck.Config):
20
+ DEFAULT_PERMITTED_CATEGORIES: ta.ClassVar[ta.AbstractSet[str]] = frozenset([
21
+ 'Lu', # Letter, uppercase
22
+ 'Ll', # Letter, lowercase
23
+ 'Lt', # Letter, titlecase
24
+ 'Sm', # Symbol, math
25
+ 'Sc', # Symbol, currency
26
+ 'Sk', # Symbol, modifier
27
+ 'So', # Symbol, other
28
+ ])
29
+
30
+ permitted_categories: ta.AbstractSet[str] = DEFAULT_PERMITTED_CATEGORIES
31
+
32
+ def __init__(
33
+ self,
34
+ context: PrecheckContext,
35
+ config: Config = Config(),
36
+ *,
37
+ dir_walk_cache: DirWalkCache,
38
+ text_file_cache: TextFileCache,
39
+ headers_cache: HeadersCache,
40
+ ) -> None:
41
+ super().__init__(config)
42
+
43
+ self._context = context
44
+
45
+ self._dir_walk_cache = dir_walk_cache
46
+ self._text_file_cache = text_file_cache
47
+ self._headers_cache = headers_cache
48
+
49
+ async def _run_py_file(self, py_file: str) -> ta.AsyncGenerator[Precheck.Violation]:
50
+ if isinstance(header_lines := self._headers_cache.get_file_headers(py_file), Exception):
51
+ return
52
+ if any(hl.src.strip() == '# @omlish-precheck-allow-any-unicode' for hl in header_lines):
53
+ return
54
+
55
+ src = self._text_file_cache.get_entry(py_file).text()
56
+
57
+ illegal_chars = {
58
+ ch
59
+ for ch in src
60
+ if ord(ch) > 255 and
61
+ unicodedata.category(ch) not in self._config.permitted_categories
62
+ }
63
+
64
+ if illegal_chars:
65
+ sl = [
66
+ f'({ch!r}, {unicodedata.category(ch)})'
67
+ for ch in sorted(illegal_chars)
68
+ ]
69
+ yield Precheck.Violation(self, f'source file {py_file} has illegal unicode characters: {", ".join(sl)}')
70
+
71
+ async def run(self) -> ta.AsyncGenerator[Precheck.Violation]:
72
+ py_files = [
73
+ os.path.join(e.root, f)
74
+ for src_root in self._context.src_roots
75
+ for e in self._dir_walk_cache.list_dir(src_root)
76
+ for f in e.files
77
+ if f.endswith('.py')
78
+ ]
79
+ for py_file in sorted(py_files):
80
+ async for v in self._run_py_file(py_file):
81
+ yield v
@@ -1,3 +1,4 @@
1
+ # @omlish-precheck-allow-any-unicode
1
2
  import typing as ta
2
3
 
3
4
  from ... import ptk
@@ -1,3 +1,4 @@
1
+ # @omlish-precheck-allow-any-unicode
1
2
  import enum
2
3
  import typing as ta
3
4
 
omdev/pyproject/cexts.py CHANGED
@@ -12,7 +12,7 @@ name (str) -
12
12
  sources (list[str]) -
13
13
  list of source filenames, relative to the distribution root (where the setup script lives), in Unix form
14
14
  (slash-separated) for portability. Source files may be C, C++, SWIG (.i), platform-specific resource files, or
15
- whatever else is recognized by the build_ext command as source for a Python extension.
15
+ whatever else is recognized by the "build_ext" command as source for a Python extension.
16
16
 
17
17
  include_dirs (list[str]) -
18
18
  list of directories to search for C/C++ header files (in Unix form for portability)
@@ -20,7 +20,7 @@ include_dirs (list[str]) -
20
20
  define_macros (list[tuple[str, str|None]]) -
21
21
  list of macros to define; each macro is defined using a 2-tuple: the first item corresponding to the name of the
22
22
  macro and the second item either a string with its value or None to define it without a particular value (equivalent
23
- of “#define FOO in source or -DFOO on Unix C compiler command line)
23
+ of "#define FOO" in source or -DFOO on Unix C compiler command line)
24
24
 
25
25
  undef_macros (list[str]) -
26
26
  list of macros to undefine explicitly
@@ -41,7 +41,7 @@ extra_objects (list[str]) -
41
41
 
42
42
  extra_compile_args (list[str]) -
43
43
  any extra platform- and compiler-specific information to use when compiling the source files in 'sources'. For
44
- platforms and compilers where command line makes sense, this is typically a list of command-line arguments, but
44
+ platforms and compilers where "command line" makes sense, this is typically a list of command-line arguments, but
45
45
  for other platforms it could be anything.
46
46
 
47
47
  extra_link_args (list[str]) -
@@ -50,7 +50,7 @@ extra_link_args (list[str]) -
50
50
 
51
51
  export_symbols (list[str]) -
52
52
  list of symbols to be exported from a shared extension. Not used on all platforms, and not generally necessary for
53
- Python extensions, which typically export exactly one symbol: init + extension_name.
53
+ Python extensions, which typically export exactly one symbol: "init" + extension_name.
54
54
 
55
55
  swig_opts (list[str]) -
56
56
  any extra options to pass to SWIG if a source file has the .i extension.
@@ -59,7 +59,7 @@ depends (list[str]) -
59
59
  list of files that the extension depends on
60
60
 
61
61
  language (str) -
62
- extension language (i.e. c”, c++”, objc). Will be detected from the source extensions if not provided.
62
+ extension language (i.e. "c", "c++", "objc"). Will be detected from the source extensions if not provided.
63
63
 
64
64
  optional (bool) -
65
65
  specifies that a build failure in the extension should not abort the build process, but simply not install the
omdev/scripts/ci.py CHANGED
@@ -4753,7 +4753,6 @@ inj = InjectionApi()
4753
4753
  """
4754
4754
  TODO:
4755
4755
  - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
4756
- - literals
4757
4756
  - Options.sequence_cls = list, mapping_cls = dict, ... - def with_mutable_containers() -> Options
4758
4757
  """
4759
4758
 
@@ -4867,6 +4866,28 @@ class OptionalObjMarshaler(ObjMarshaler):
4867
4866
  return self.item.unmarshal(o, ctx)
4868
4867
 
4869
4868
 
4869
+ @dc.dataclass(frozen=True)
4870
+ class PrimitiveUnionObjMarshaler(ObjMarshaler):
4871
+ pt: ta.Tuple[type, ...]
4872
+ x: ta.Optional[ObjMarshaler] = None
4873
+
4874
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
4875
+ if isinstance(o, self.pt):
4876
+ return o
4877
+ elif self.x is not None:
4878
+ return self.x.marshal(o, ctx)
4879
+ else:
4880
+ raise TypeError(o)
4881
+
4882
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
4883
+ if isinstance(o, self.pt):
4884
+ return o
4885
+ elif self.x is not None:
4886
+ return self.x.unmarshal(o, ctx)
4887
+ else:
4888
+ raise TypeError(o)
4889
+
4890
+
4870
4891
  @dc.dataclass(frozen=True)
4871
4892
  class LiteralObjMarshaler(ObjMarshaler):
4872
4893
  item: ObjMarshaler
@@ -5065,6 +5086,13 @@ _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES: ta.Dict[ta.Any, type] = {
5065
5086
  collections.abc.MutableSequence: list,
5066
5087
  }
5067
5088
 
5089
+ _OBJ_MARSHALER_PRIMITIVE_TYPES: ta.Set[type] = {
5090
+ int,
5091
+ float,
5092
+ bool,
5093
+ str,
5094
+ }
5095
+
5068
5096
 
5069
5097
  ##
5070
5098
 
@@ -5213,8 +5241,16 @@ class ObjMarshalerManager:
5213
5241
 
5214
5242
  if is_literal_type(ty):
5215
5243
  lvs = frozenset(get_literal_type_args(ty))
5244
+ if None in lvs:
5245
+ is_opt = True
5246
+ lvs -= frozenset([None])
5247
+ else:
5248
+ is_opt = False
5216
5249
  lty = check.single(set(map(type, lvs)))
5217
- return LiteralObjMarshaler(rec(lty), lvs)
5250
+ lm: ObjMarshaler = LiteralObjMarshaler(rec(lty), lvs)
5251
+ if is_opt:
5252
+ lm = OptionalObjMarshaler(lm)
5253
+ return lm
5218
5254
 
5219
5255
  if is_generic_alias(ty):
5220
5256
  try:
@@ -5234,7 +5270,31 @@ class ObjMarshalerManager:
5234
5270
  return IterableObjMarshaler(st, rec(e))
5235
5271
 
5236
5272
  if is_union_alias(ty):
5237
- return OptionalObjMarshaler(rec(get_optional_alias_arg(ty)))
5273
+ uts = frozenset(ta.get_args(ty))
5274
+ if None in uts or type(None) in uts:
5275
+ is_opt = True
5276
+ uts = frozenset(ut for ut in uts if ut not in (None, type(None)))
5277
+ else:
5278
+ is_opt = False
5279
+
5280
+ um: ObjMarshaler
5281
+ if not uts:
5282
+ raise TypeError(ty)
5283
+ elif len(uts) == 1:
5284
+ um = rec(check.single(uts))
5285
+ else:
5286
+ pt = tuple({ut for ut in uts if ut in _OBJ_MARSHALER_PRIMITIVE_TYPES})
5287
+ np_uts = {ut for ut in uts if ut not in _OBJ_MARSHALER_PRIMITIVE_TYPES}
5288
+ if not np_uts:
5289
+ um = PrimitiveUnionObjMarshaler(pt)
5290
+ elif len(np_uts) == 1:
5291
+ um = PrimitiveUnionObjMarshaler(pt, x=rec(check.single(np_uts)))
5292
+ else:
5293
+ raise TypeError(ty)
5294
+
5295
+ if is_opt:
5296
+ um = OptionalObjMarshaler(um)
5297
+ return um
5238
5298
 
5239
5299
  raise TypeError(ty)
5240
5300
 
@@ -5508,7 +5508,6 @@ inj = InjectionApi()
5508
5508
  """
5509
5509
  TODO:
5510
5510
  - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
5511
- - literals
5512
5511
  - Options.sequence_cls = list, mapping_cls = dict, ... - def with_mutable_containers() -> Options
5513
5512
  """
5514
5513
 
@@ -5622,6 +5621,28 @@ class OptionalObjMarshaler(ObjMarshaler):
5622
5621
  return self.item.unmarshal(o, ctx)
5623
5622
 
5624
5623
 
5624
+ @dc.dataclass(frozen=True)
5625
+ class PrimitiveUnionObjMarshaler(ObjMarshaler):
5626
+ pt: ta.Tuple[type, ...]
5627
+ x: ta.Optional[ObjMarshaler] = None
5628
+
5629
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
5630
+ if isinstance(o, self.pt):
5631
+ return o
5632
+ elif self.x is not None:
5633
+ return self.x.marshal(o, ctx)
5634
+ else:
5635
+ raise TypeError(o)
5636
+
5637
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
5638
+ if isinstance(o, self.pt):
5639
+ return o
5640
+ elif self.x is not None:
5641
+ return self.x.unmarshal(o, ctx)
5642
+ else:
5643
+ raise TypeError(o)
5644
+
5645
+
5625
5646
  @dc.dataclass(frozen=True)
5626
5647
  class LiteralObjMarshaler(ObjMarshaler):
5627
5648
  item: ObjMarshaler
@@ -5820,6 +5841,13 @@ _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES: ta.Dict[ta.Any, type] = {
5820
5841
  collections.abc.MutableSequence: list,
5821
5842
  }
5822
5843
 
5844
+ _OBJ_MARSHALER_PRIMITIVE_TYPES: ta.Set[type] = {
5845
+ int,
5846
+ float,
5847
+ bool,
5848
+ str,
5849
+ }
5850
+
5823
5851
 
5824
5852
  ##
5825
5853
 
@@ -5968,8 +5996,16 @@ class ObjMarshalerManager:
5968
5996
 
5969
5997
  if is_literal_type(ty):
5970
5998
  lvs = frozenset(get_literal_type_args(ty))
5999
+ if None in lvs:
6000
+ is_opt = True
6001
+ lvs -= frozenset([None])
6002
+ else:
6003
+ is_opt = False
5971
6004
  lty = check.single(set(map(type, lvs)))
5972
- return LiteralObjMarshaler(rec(lty), lvs)
6005
+ lm: ObjMarshaler = LiteralObjMarshaler(rec(lty), lvs)
6006
+ if is_opt:
6007
+ lm = OptionalObjMarshaler(lm)
6008
+ return lm
5973
6009
 
5974
6010
  if is_generic_alias(ty):
5975
6011
  try:
@@ -5989,7 +6025,31 @@ class ObjMarshalerManager:
5989
6025
  return IterableObjMarshaler(st, rec(e))
5990
6026
 
5991
6027
  if is_union_alias(ty):
5992
- return OptionalObjMarshaler(rec(get_optional_alias_arg(ty)))
6028
+ uts = frozenset(ta.get_args(ty))
6029
+ if None in uts or type(None) in uts:
6030
+ is_opt = True
6031
+ uts = frozenset(ut for ut in uts if ut not in (None, type(None)))
6032
+ else:
6033
+ is_opt = False
6034
+
6035
+ um: ObjMarshaler
6036
+ if not uts:
6037
+ raise TypeError(ty)
6038
+ elif len(uts) == 1:
6039
+ um = rec(check.single(uts))
6040
+ else:
6041
+ pt = tuple({ut for ut in uts if ut in _OBJ_MARSHALER_PRIMITIVE_TYPES})
6042
+ np_uts = {ut for ut in uts if ut not in _OBJ_MARSHALER_PRIMITIVE_TYPES}
6043
+ if not np_uts:
6044
+ um = PrimitiveUnionObjMarshaler(pt)
6045
+ elif len(np_uts) == 1:
6046
+ um = PrimitiveUnionObjMarshaler(pt, x=rec(check.single(np_uts)))
6047
+ else:
6048
+ raise TypeError(ty)
6049
+
6050
+ if is_opt:
6051
+ um = OptionalObjMarshaler(um)
6052
+ return um
5993
6053
 
5994
6054
  raise TypeError(ty)
5995
6055
 
omdev/tools/git/cli.py CHANGED
@@ -1,4 +1,5 @@
1
1
  # ruff: noqa: UP006 UP007 UP045
2
+ # @omlish-precheck-allow-any-unicode
2
3
  """
3
4
  TODO:
4
5
  - Handle:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omdev
3
- Version: 0.0.0.dev371
3
+ Version: 0.0.0.dev373
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.13
14
14
  License-File: LICENSE
15
- Requires-Dist: omlish==0.0.0.dev371
15
+ Requires-Dist: omlish==0.0.0.dev373
16
16
  Provides-Extra: all
17
17
  Requires-Dist: black~=25.1; extra == "all"
18
18
  Requires-Dist: pycparser~=2.22; extra == "all"
@@ -1,4 +1,4 @@
1
- omdev/.manifests.json,sha256=_LJ5KSvdCmh-6Pp6pXzha4pbPOwD3F_QQNH2JpewWHk,11870
1
+ omdev/.manifests.json,sha256=cprM065HGO3vKL_ru2fzUGi-J3goZ_jMLkm_B3qGM94,11863
2
2
  omdev/__about__.py,sha256=_c693iMsl07FKXJ_2zrWiE8t84SSbDxlCrash1rJR7M,1202
3
3
  omdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omdev/cmake.py,sha256=9rfSvFHPmKDj9ngvfDB2vK8O-xO_ZwUm7hMKLWA-yOw,4578
@@ -18,7 +18,7 @@ omdev/amalg/types.py,sha256=BXXJI0VctKTsZv_wXiyMMq3-xShxZ1ak0wxXUK8n9_g,89
18
18
  omdev/amalg/typing.py,sha256=oLlkCnnZQkyKM_xxqm9uxECjn9xj0c4MIwYxxItBdPY,2191
19
19
  omdev/cache/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  omdev/cache/compute/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- omdev/cache/compute/cache.py,sha256=IHF1959RS1z-aUuUaFOTjqcIjckbVgNEA7dONfh6y-o,3628
21
+ omdev/cache/compute/cache.py,sha256=7cJmxiHwbnitMZaLZEZVjEq0v1JJN7HKBK-BH_EtVY4,3628
22
22
  omdev/cache/compute/contexts.py,sha256=OKOHOIj8JSEHmCKMY9G_zpKkLAkGuXcFXvzY5SXqCMA,3231
23
23
  omdev/cache/compute/currents.py,sha256=D1Ls5Sd7FX2aPO5wpyEwTSmkz50sxkwCs37Amb0urH8,1425
24
24
  omdev/cache/compute/fns.py,sha256=_1xU7qw1O57OsTp17RFjFTBO1M2t54RL-CGdl-0cTBk,4175
@@ -132,6 +132,10 @@ omdev/home/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
132
132
  omdev/home/paths.py,sha256=tnRuEzA1nRc9NJKGSxyZMOLlZBk5Da0AQhNeSmBozYA,1331
133
133
  omdev/home/secrets.py,sha256=XarB-uhwjRc3dpT5ZF-9yoRPDr7kpyr4Hgs0I4kKL4I,1992
134
134
  omdev/home/shadow.py,sha256=6mGDJV70IExa-nzd7mOUW4R3OZIXO0fBwmOUU9T9f4M,295
135
+ omdev/intellij/__init__.py,sha256=OkihYdld_LTk_gTcyzOWc9Nze_drjsIYMYpbA5DG6m4,132
136
+ omdev/intellij/cli.py,sha256=Sds9Xj_OHgbm76j70frhwrZEzlVi-1Rd93g0ypIe6t4,2038
137
+ omdev/intellij/ides.py,sha256=ll-NKTKeS6ZgcWB3xYXr3gxGFgNOc0ZPulTimHkvU9Y,1573
138
+ omdev/intellij/open.py,sha256=3HlbJsGkwZ28UBi1P7dVJMRDMLbJyOGQs9QMQqgYVus,2679
135
139
  omdev/interp/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
136
140
  omdev/interp/__main__.py,sha256=GMCqeGYltgt5dlJzHxY9gqisa8cRkrPfmZYuZnjg4WI,162
137
141
  omdev/interp/cli.py,sha256=xYZHts_tYuWPenTTLQIQ-I1JWHao6XolsH0R73AufDY,2380
@@ -198,11 +202,12 @@ omdev/precheck/__main__.py,sha256=UEuS4z5-heIrwTtB-ONe1KeXJdqj8tYXMqWMpuO10so,16
198
202
  omdev/precheck/base.py,sha256=fKdrfakq2u1UU1_JZFnl-non9bIAZMsSkVY1SMYn8xQ,662
199
203
  omdev/precheck/caches.py,sha256=OZKP20DIj6OpUzdNwrjCufv1GzndEbsc7tLD-qHNv9g,1736
200
204
  omdev/precheck/git.py,sha256=O8rNQZ_vlHec0pOFbK6LOkbly5ZIUYT_HXRMqQX8GaI,774
201
- omdev/precheck/imports.py,sha256=JS-j1YWi_4YL43053xITl6b35isRW-7ChoYoztufVn0,2549
205
+ omdev/precheck/imports.py,sha256=3loQxHMrpI0ce4-le77NCSxutLac_5vDW4UDX7KWWg8,2565
202
206
  omdev/precheck/lite.py,sha256=qd6nXWEVut8aBSRD_NxnxXGRNa9ue8mu8ND8rGLisE4,4710
203
- omdev/precheck/main.py,sha256=VEPdq9n8Yo_HiDqfCdHH1pj3xuJgOzrXie_vWoNbKqw,4303
204
- omdev/precheck/manifests.py,sha256=EZkChCc9aDlAZswVVvq696p4r3Vy7VBQUSfn6DXhUcc,778
205
- omdev/precheck/scripts.py,sha256=SyHTVUVMTnBoTzcrWywfwxuUoIfpeV_EQcPQZyaht6c,1336
207
+ omdev/precheck/main.py,sha256=_1A5wiu9p2th1dn_17w1ZIFtMmCIOaTFpWyvK0jopEA,4374
208
+ omdev/precheck/manifests.py,sha256=1CG0PG0feagydT-cgxiOBvQKhoILjZVXk4MYf65wkkM,786
209
+ omdev/precheck/scripts.py,sha256=244Jq5xee2QErn0gvpt0hmdYg95TYtuefMDXaE9YPws,1344
210
+ omdev/precheck/unicode.py,sha256=VUNDCrlfUas_U8ugV_q0eFUXuBgKjS8YdCFm0FXREXo,2583
206
211
  omdev/ptk/__init__.py,sha256=IemfBhxxqZe1GeZ2_IMQzqWAB5j0XyARp_aajnonyKY,5142
207
212
  omdev/ptk/confirm.py,sha256=kObMUAu-EZC0vdT9LLwtbNA6YLLNmn-Uy18SQTWBTb8,1471
208
213
  omdev/ptk/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -215,8 +220,8 @@ omdev/ptk/markdown/cli.py,sha256=FlV12qpyVWgpfeOzyFg9DlgLkss3rgCet_Jebs6_Xqo,537
215
220
  omdev/ptk/markdown/markdown.py,sha256=wRxCWSV2u71NP_MTi8A8vVLjZ1TmmaWUZMnvW468Ejc,12368
216
221
  omdev/ptk/markdown/parser.py,sha256=UppwouvAYh3qzQMKL-BjcyqBIl2KHcocXlWiKFZ7cBA,990
217
222
  omdev/ptk/markdown/styles.py,sha256=lc17zooXhff5_2tkqLsCmdq2b_rfSAehmHVR-8Lo2qk,777
218
- omdev/ptk/markdown/tags.py,sha256=askGU252Zu8KxG2menVHZHXZ4fGbItgy0G4UW9F3mFI,6983
219
- omdev/ptk/markdown/utils.py,sha256=tzbYAZ-sloEoIguXvhaOC4w2y7AKh0vtC4_a2Ovf1D8,10476
223
+ omdev/ptk/markdown/tags.py,sha256=6wF9lbqbpf_WCxEJ_tJP_QtaDCcQKCKYS1CMzHTQg2U,7020
224
+ omdev/ptk/markdown/utils.py,sha256=bwhmV-L4wjLuNLZc-ieWioL1G-EmonmhB031pvpIejw,10513
220
225
  omdev/py/__init__.py,sha256=u7tLEfRTnPVtWPmKK_ZIvnFkZwTe1q44UQ53cvsix3k,41
221
226
  omdev/py/attrdocs.py,sha256=FcMSUFw8_zyNYducsWnZKEKi_WDox3-b3Xjw0DrHUcs,5180
222
227
  omdev/py/bracepy.py,sha256=PPPoTMj4FJ5Lk3GGPEEWBGU7Kl1eCJBApSJnRvRj2Ms,2753
@@ -241,7 +246,7 @@ omdev/py/tools/importscan.py,sha256=4dCH0coX0OqNwesteKaTE8GxuSfLhgXYQlzNUXLiSNY,
241
246
  omdev/py/tools/mkrelimp.py,sha256=kuzfS4GWcGi-ItyhEHSW4hV9ZcfDJL6inDMhDp1V2lE,4049
242
247
  omdev/pyproject/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
243
248
  omdev/pyproject/__main__.py,sha256=gn3Rl1aYPYdiTtEqa9ifi0t-e4ZwPY0vhJ4UXvYdJDY,165
244
- omdev/pyproject/cexts.py,sha256=4xYt1aw2FqcPEpdr1clJnEmp3sbEQXnSWl1sm-4d9NE,4292
249
+ omdev/pyproject/cexts.py,sha256=GLD4fe61M_fHhdMcKlcQNUoCb7MeVXY6Fw-grKH4hTU,4264
245
250
  omdev/pyproject/cli.py,sha256=Umsu2bcJUYeeVXICaZFhKckUBT6VWuYDL4htgCGGQIs,8749
246
251
  omdev/pyproject/configs.py,sha256=baNRwHtUW8S8DKCxuKlMbV3Gduujd1PyNURxQ48Nnxk,2813
247
252
  omdev/pyproject/inject.py,sha256=Von8_8ofkITLoCEwDHNRAwY0AEdFQg7r2ILS8kcTMuY,325
@@ -252,9 +257,9 @@ omdev/pyproject/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
252
257
  omdev/pyproject/resources/docker-dev.sh,sha256=DHkz5D18jok_oDolfg2mqrvGRWFoCe9GQo04dR1czcc,838
253
258
  omdev/pyproject/resources/python.sh,sha256=rFaN4SiJ9hdLDXXsDTwugI6zsw6EPkgYMmtacZeTbvw,749
254
259
  omdev/scripts/__init__.py,sha256=MKCvUAEQwsIvwLixwtPlpBqmkMXLCnjjXyAXvVpDwVk,91
255
- omdev/scripts/ci.py,sha256=tzVEK-JKZ4WNDGAziUbXpw_HEuYGQiMhsxUmM-usvu0,360136
260
+ omdev/scripts/ci.py,sha256=R-zzmbR38A68_5Qvfo8RRnyeBhBej6FwB4a7LZMNvqw,362103
256
261
  omdev/scripts/interp.py,sha256=T8EdNxuncwqCXfIBfCLoUcsYNqEhOrOE8V1hC9vTzqI,158095
257
- omdev/scripts/pyproject.py,sha256=qBBXmbdKMYzqxnb0zEWYhCOg9Cy0pWno3wjulICFj_o,267626
262
+ omdev/scripts/pyproject.py,sha256=Hwy7pK9-_qk3Uayer5kqDWeS8T5OSo0kZZzPqPXUA8U,269593
258
263
  omdev/scripts/slowcat.py,sha256=lssv4yrgJHiWfOiHkUut2p8E8Tq32zB-ujXESQxFFHY,2728
259
264
  omdev/scripts/tmpexec.py,sha256=WTYcf56Tj2qjYV14AWmV8SfT0u6Y8eIU6cKgQRvEK3c,1442
260
265
  omdev/tokens/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -266,7 +271,6 @@ omdev/tools/cloc.py,sha256=BiIf2PnnoRH6ByH4mnua69Vv-ZDPHsSAPUds7giRolI,6013
266
271
  omdev/tools/diff.py,sha256=S6ddB7pBeMtzKh2hiHfzQ93pj6mclKt1UZQMKy4Bt5M,520
267
272
  omdev/tools/doc.py,sha256=PCtqDjFA55Ed4QVUpMiw05NwyXivCeox-ZE_JDN0BD0,2575
268
273
  omdev/tools/docker.py,sha256=4TdpKiWMr9eCffAPHo6sAWGURGTYMY5AK_F5lmnwlJA,7363
269
- omdev/tools/intellij.py,sha256=yy6Uw2hcYyEWLZESAupKvd3U6omirYMbjnbw7UllhV4,5834
270
274
  omdev/tools/linehisto.py,sha256=0ZNm34EuiZBE9Q2YC6KNLNNydNT8QPSOwvYzXiU9S2Q,8881
271
275
  omdev/tools/mkenv.py,sha256=G2tu5bmiyKFyZuqtUoM7Z-6AI6CI86F2LwoIozoWOvo,2300
272
276
  omdev/tools/notebook.py,sha256=MGi2JEwyIPR1n7gakaaYZL1HHbSVmDKGQROqH56ppgU,3499
@@ -283,7 +287,7 @@ omdev/tools/antlr/consts.py,sha256=4xH4jyNE5czXRWCn65jFF0MrAodMPe_kYMWpgMxWmpo,2
283
287
  omdev/tools/antlr/gen.py,sha256=spYfyfX_r_1YT4uZ_bEhtGwANenUtZvSOFck5eiNHhQ,5122
284
288
  omdev/tools/git/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
285
289
  omdev/tools/git/__main__.py,sha256=gI87SBUgTkKUcUM-RtZWnei-UUDDqzbr5aPztb-gvbE,168
286
- omdev/tools/git/cli.py,sha256=6YG-punGydpaB65waHowMkGGH6Ded8GXi-0pZ_M3lpQ,15454
290
+ omdev/tools/git/cli.py,sha256=UxxyQcYF2--Q7UcLJFWXew8_rwLJJNENACplwdFEEKM,15491
287
291
  omdev/tools/git/consts.py,sha256=JuXivUNDkNhM4pe97icjRVAKM8cNRbrODquHINNKqOE,40
288
292
  omdev/tools/git/messages.py,sha256=NWztIK0nAKJIOVzuVQcR_5LHZUgqyVkrOlpl7dFLMdU,2424
289
293
  omdev/tools/json/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -303,9 +307,9 @@ omdev/tools/jsonview/resources/jsonview.js,sha256=faDvXDOXKvEvjOuIlz4D3F2ReQXb_b
303
307
  omdev/tools/pawk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
304
308
  omdev/tools/pawk/__main__.py,sha256=VCqeRVnqT1RPEoIrqHFSu4PXVMg4YEgF4qCQm90-eRI,66
305
309
  omdev/tools/pawk/pawk.py,sha256=ao5mdrpiSU4AZ8mBozoEaV3UVlmVTnRG9wD9XP70MZE,11429
306
- omdev-0.0.0.dev371.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
307
- omdev-0.0.0.dev371.dist-info/METADATA,sha256=j7bKLXweqn8EK5DBHrL9qUsx8ybrJe8uCYSx93TLKvo,1674
308
- omdev-0.0.0.dev371.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
309
- omdev-0.0.0.dev371.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
310
- omdev-0.0.0.dev371.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
311
- omdev-0.0.0.dev371.dist-info/RECORD,,
310
+ omdev-0.0.0.dev373.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
311
+ omdev-0.0.0.dev373.dist-info/METADATA,sha256=zGQo4AZ9GWEy7Aymxc7RH7vOJP5XlUtay1iKjAJF4q8,1674
312
+ omdev-0.0.0.dev373.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
313
+ omdev-0.0.0.dev373.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
314
+ omdev-0.0.0.dev373.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
315
+ omdev-0.0.0.dev373.dist-info/RECORD,,
omdev/tools/intellij.py DELETED
@@ -1,253 +0,0 @@
1
- """
2
- TODO:
3
- - read .idea/ for inference?
4
- - PyCharm.app/Contents/plugins/python-ce/helpers/pydev/_pydevd_bundle/pydevd_constants.py -> USE_LOW_IMPACT_MONITORING
5
- - DISPLAY=":1" - ls /tmp/.X11-unix/X1 ?
6
- - https://unix.stackexchange.com/questions/17255/is-there-a-command-to-list-all-open-displays-on-a-machine
7
- - w -oush
8
- """
9
- import dataclasses as dc
10
- import enum
11
- import inspect
12
- import os.path
13
- import shlex
14
- import subprocess
15
- import sys
16
- import tempfile
17
- import typing as ta
18
-
19
- from omlish.argparse import all as ap
20
- from omlish.diag.pycharm import get_pycharm_version
21
-
22
- from ..cli import CliModule
23
-
24
-
25
- ##
26
-
27
-
28
- class Ide(enum.Enum):
29
- PYCHARM = enum.auto()
30
- IDEA = enum.auto()
31
- CLION = enum.auto()
32
- WEBSTORM = enum.auto()
33
- GOLAND = enum.auto()
34
-
35
-
36
- ##
37
-
38
-
39
- _INFER_FILE_NAME_SETS_BY_IDE: ta.Mapping[Ide, ta.AbstractSet[str]] = {
40
- Ide.PYCHARM: frozenset([
41
- 'setup.py',
42
- 'setup.cfg',
43
- 'pyproject.toml',
44
- 'requirements.txt',
45
- 'Pipfile',
46
- 'poetry.lock',
47
- '.python-version',
48
- 'wsgi.py',
49
- 'asgi.py',
50
- 'manage.py',
51
- ]),
52
- Ide.IDEA: frozenset([
53
- 'pom.xml',
54
- 'mvnw',
55
- 'build.gradle',
56
- 'build.gradle.kts',
57
- 'gradlew',
58
- 'module-info.java',
59
- '.java-version',
60
- ]),
61
- Ide.CLION: frozenset([
62
- 'CMakeLists.txt',
63
- 'configure.ac',
64
- 'configure.in',
65
- 'config.h.in',
66
- 'vcpkg.json',
67
- ]),
68
- Ide.WEBSTORM: frozenset([
69
- 'package.json',
70
- 'package-lock.json',
71
- ]),
72
- Ide.GOLAND: frozenset([
73
- 'go.mod',
74
- 'go.sum',
75
- ]),
76
- }
77
-
78
-
79
- def _infer_directory_ide(cwd: str | None) -> Ide | None:
80
- if cwd is None:
81
- cwd = os.getcwd()
82
-
83
- for i, fs in _INFER_FILE_NAME_SETS_BY_IDE.items():
84
- for f in fs:
85
- if os.path.exists(os.path.join(cwd, f)):
86
- return i
87
-
88
- return None
89
-
90
-
91
- ##
92
-
93
-
94
- def _get_ide_version(ide: Ide) -> str:
95
- if ide is Ide.PYCHARM:
96
- return get_pycharm_version()
97
- else:
98
- raise ValueError(ide)
99
-
100
-
101
- ##
102
-
103
-
104
- _DARWIN_OPEN_SCRIPT_APP_BY_IDE: ta.Mapping[Ide, str] = {
105
- Ide.PYCHARM: 'PyCharm',
106
- Ide.CLION: 'CLion',
107
- Ide.IDEA: 'IntelliJ IDEA',
108
- Ide.WEBSTORM: 'WebStorm',
109
- Ide.GOLAND: 'GoLand',
110
- }
111
-
112
- _DARWIN_OPEN_SCRIPT = """
113
- tell application "{app}"
114
- activate
115
- open "{dir}"
116
- end tell
117
- return
118
- """
119
-
120
-
121
- ##
122
-
123
-
124
- _LINUX_WM_CLASS_BY_IDE: ta.Mapping[Ide, str] = {
125
- Ide.PYCHARM: 'jetbrains-pycharm.jetbrains-pycharm',
126
- Ide.CLION: 'jetbrains-clion.jetbrains-clion',
127
- Ide.IDEA: 'jetbrains-idea.jetbrains-idea',
128
- Ide.WEBSTORM: 'jetbrains-webstorm.jetbrains-webstorm',
129
- Ide.GOLAND: 'jetbrains-goland.jetbrains-goland',
130
- }
131
-
132
-
133
- # sudo apt install xdotool wmctrl
134
- # TODO: nohup pycharm.sh "$PROJECT_PATH" > /dev/null 2>&1 & ?
135
- _LINUX_OPEN_SCRIPT = """
136
- xdotool key --delay 1000 alt+f alt+o
137
- xdotool type --delay 1000 {dir}
138
- xdotool key Return
139
- """
140
-
141
-
142
- @dc.dataclass(frozen=True)
143
- class WmctrlLine:
144
- window_id: str
145
- desktop_id: str
146
- pid: str
147
- wm_class: str
148
- user: str
149
- title: str
150
-
151
-
152
- # TODO: https://stackoverflow.com/a/79016360
153
- def parse_wmctrl_lxp_line(l: str) -> WmctrlLine:
154
- return WmctrlLine(*l.strip().split(maxsplit=5))
155
-
156
-
157
- ##
158
-
159
-
160
- class IntellijCli(ap.Cli):
161
- @ap.cmd(
162
- ap.arg('python-exe'),
163
- ap.arg('args', nargs=ap.REMAINDER),
164
- )
165
- def pycharm_runhack(self) -> int:
166
- if not os.path.isfile(exe := self.args.python_exe):
167
- raise FileNotFoundError(exe)
168
-
169
- from omlish.diag._pycharm import runhack # noqa
170
- src = inspect.getsource(runhack)
171
-
172
- src_file = tempfile.mktemp(__package__ + '-runhack') # noqa
173
- with open(src_file, 'w') as f:
174
- f.write(src)
175
-
176
- proc = subprocess.run([exe, src_file, *self.args.args], check=False)
177
- return proc.returncode
178
-
179
- #
180
-
181
- def _get_ide(
182
- self,
183
- *,
184
- cwd: str | None = None,
185
- ) -> Ide: # noqa
186
- if (ai := self.args.ide) is not None:
187
- return Ide[ai.upper()] # noqa
188
-
189
- if (ii := _infer_directory_ide(cwd)) is not None:
190
- return ii
191
-
192
- return Ide.PYCHARM
193
-
194
- @ap.cmd(
195
- ap.arg('-e', '--ide'),
196
- )
197
- def version(self) -> None:
198
- i = self._get_ide()
199
- v = _get_ide_version(i)
200
- print(v)
201
-
202
- @ap.cmd(
203
- ap.arg('dir', nargs='?'),
204
- ap.arg('-e', '--ide'),
205
- )
206
- def open(self) -> None:
207
- dir = os.path.abspath(self.args.dir or '.') # noqa
208
- ide = self._get_ide(cwd=dir)
209
-
210
- if (plat := sys.platform) == 'darwin':
211
- if '"' in dir:
212
- raise ValueError(dir)
213
-
214
- scpt_src = _DARWIN_OPEN_SCRIPT.format(
215
- app=_DARWIN_OPEN_SCRIPT_APP_BY_IDE[ide],
216
- dir=dir,
217
- )
218
-
219
- scpt_file = tempfile.mktemp(__package__ + '-intellij-open') # noqa
220
- with open(scpt_file, 'w') as f:
221
- f.write(scpt_src)
222
-
223
- subprocess.check_call(['osascript', scpt_file])
224
-
225
- elif plat == 'linux':
226
- env = os.environ
227
- env.setdefault('DISPLAY', ':1')
228
-
229
- wmc_out = subprocess.check_output(['wmctrl', '-lxp'], env=env).decode()
230
- wls = [parse_wmctrl_lxp_line(l) for l in wmc_out.splitlines()]
231
- tgt_wmc = _LINUX_WM_CLASS_BY_IDE[ide]
232
- tgt_wl = next(wl for wl in wls if wl.wm_class == tgt_wmc)
233
- subprocess.check_call(['wmctrl', '-ia', tgt_wl.window_id], env=env)
234
-
235
- scpt = _LINUX_OPEN_SCRIPT.format(dir=shlex.quote(os.path.expanduser(dir)))
236
- print(scpt)
237
- # subprocess.check_call(scpt, shell=True, env=env) # noqa
238
- raise NotImplementedError
239
-
240
- else:
241
- raise OSError(plat)
242
-
243
-
244
- def _main() -> None:
245
- IntellijCli()(exit=True)
246
-
247
-
248
- # @omlish-manifest
249
- _CLI_MODULE = CliModule(['intellij', 'ij'], __name__)
250
-
251
-
252
- if __name__ == '__main__':
253
- _main()