omdev 0.0.0.dev28__tar.gz → 0.0.0.dev29__tar.gz

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.

Files changed (110) hide show
  1. {omdev-0.0.0.dev28/omdev.egg-info → omdev-0.0.0.dev29}/PKG-INFO +2 -2
  2. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/comp/contexts.py +1 -1
  3. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/data/__init__.py +1 -1
  4. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/data/cache.py +1 -1
  5. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/data/defaults.py +3 -3
  6. omdev-0.0.0.dev29/omdev/precheck/base.py +37 -0
  7. omdev-0.0.0.dev29/omdev/precheck/git.py +34 -0
  8. omdev-0.0.0.dev29/omdev/precheck/lite.py +135 -0
  9. omdev-0.0.0.dev29/omdev/precheck/precheck.py +100 -0
  10. omdev-0.0.0.dev29/omdev/precheck/scripts.py +42 -0
  11. omdev-0.0.0.dev29/omdev/tools/piptools.py +26 -0
  12. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29/omdev.egg-info}/PKG-INFO +2 -2
  13. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev.egg-info/SOURCES.txt +5 -0
  14. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev.egg-info/requires.txt +1 -1
  15. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/pyproject.toml +2 -2
  16. omdev-0.0.0.dev28/omdev/precheck/precheck.py +0 -316
  17. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/LICENSE +0 -0
  18. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/MANIFEST.in +0 -0
  19. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/README.rst +0 -0
  20. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/.manifests.json +0 -0
  21. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/__about__.py +0 -0
  22. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/__init__.py +0 -0
  23. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/amalg/__init__.py +0 -0
  24. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/amalg/__main__.py +0 -0
  25. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/amalg/amalg.py +0 -0
  26. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/bracepy.py +0 -0
  27. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/__init__.py +0 -0
  28. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/comp/__init__.py +0 -0
  29. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/comp/cache.py +0 -0
  30. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/comp/fns.py +0 -0
  31. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/comp/resolvers.py +0 -0
  32. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/comp/types.py +0 -0
  33. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/data/actions.py +0 -0
  34. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/data/consts.py +0 -0
  35. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/data/manifests.py +0 -0
  36. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cache/data/specs.py +0 -0
  37. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/__init__.py +0 -0
  38. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_boilerplate.cc +0 -0
  39. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/LICENSE +0 -0
  40. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/__init__.py +0 -0
  41. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/build_ext.py +0 -0
  42. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/compilers/__init__.py +0 -0
  43. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/compilers/ccompiler.py +0 -0
  44. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/compilers/options.py +0 -0
  45. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/compilers/unixccompiler.py +0 -0
  46. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/dir_util.py +0 -0
  47. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/errors.py +0 -0
  48. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/extension.py +0 -0
  49. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/file_util.py +0 -0
  50. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/modified.py +0 -0
  51. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/spawn.py +0 -0
  52. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/sysconfig.py +0 -0
  53. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/util.py +0 -0
  54. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/_distutils/version.py +0 -0
  55. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/build.py +0 -0
  56. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/cmake.py +0 -0
  57. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/importhook.py +0 -0
  58. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/magic.py +0 -0
  59. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cexts/scan.py +0 -0
  60. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/classdot.py +0 -0
  61. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/cmake.py +0 -0
  62. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/findimports.py +0 -0
  63. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/findmagic.py +0 -0
  64. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/git.py +0 -0
  65. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/interp/__init__.py +0 -0
  66. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/interp/__main__.py +0 -0
  67. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/interp/cli.py +0 -0
  68. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/interp/inspect.py +0 -0
  69. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/interp/providers.py +0 -0
  70. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/interp/pyenv.py +0 -0
  71. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/interp/resolvers.py +0 -0
  72. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/interp/standalone.py +0 -0
  73. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/interp/system.py +0 -0
  74. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/interp/types.py +0 -0
  75. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/manifests.py +0 -0
  76. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/mypy/__init__.py +0 -0
  77. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/mypy/debug.py +0 -0
  78. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/precheck/__init__.py +0 -0
  79. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/precheck/__main__.py +0 -0
  80. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/pyproject/__init__.py +0 -0
  81. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/pyproject/__main__.py +0 -0
  82. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/pyproject/cexts.py +0 -0
  83. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/pyproject/cli.py +0 -0
  84. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/pyproject/configs.py +0 -0
  85. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/pyproject/pkg.py +0 -0
  86. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/pyproject/reqs.py +0 -0
  87. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/revisions.py +0 -0
  88. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/scripts/__init__.py +0 -0
  89. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/scripts/bumpversion.py +0 -0
  90. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/scripts/execrss.py +0 -0
  91. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/scripts/interp.py +0 -0
  92. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/scripts/pyproject.py +0 -0
  93. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/tokens.py +0 -0
  94. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/toml/__init__.py +0 -0
  95. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/toml/parser.py +0 -0
  96. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/toml/writer.py +0 -0
  97. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/tools/__init__.py +0 -0
  98. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/tools/dockertools.py +0 -0
  99. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/tools/gittools.py +0 -0
  100. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/tools/importscan.py +0 -0
  101. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/tools/importtrace.py +0 -0
  102. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/tools/rst.py +0 -0
  103. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/tools/sqlrepl.py +0 -0
  104. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/versioning/__init__.py +0 -0
  105. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/versioning/specifiers.py +0 -0
  106. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/versioning/versions.py +0 -0
  107. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev/wheelfile.py +0 -0
  108. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev.egg-info/dependency_links.txt +0 -0
  109. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/omdev.egg-info/top_level.txt +0 -0
  110. {omdev-0.0.0.dev28 → omdev-0.0.0.dev29}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev28
3
+ Version: 0.0.0.dev29
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.dev28
15
+ Requires-Dist: omlish==0.0.0.dev29
16
16
  Provides-Extra: all
17
17
  Requires-Dist: pycparser~=2.22; extra == "all"
18
18
  Requires-Dist: cffi~=1.17; extra == "all"
@@ -71,7 +71,7 @@ class CacheableContext(lang.Final):
71
71
  return self._key
72
72
 
73
73
  @property
74
- def parent(self) -> 'CacheableContext | None':
74
+ def parent(self) -> ta.Optional['CacheableContext']:
75
75
  return self._parent
76
76
 
77
77
  @property
@@ -1,5 +1,5 @@
1
1
  from .cache import ( # noqa
2
- DataCache,
2
+ Cache,
3
3
  )
4
4
 
5
5
  from .defaults import ( # noqa
@@ -32,7 +32,7 @@ log = logging.getLogger(__name__)
32
32
  ##
33
33
 
34
34
 
35
- class DataCache:
35
+ class Cache:
36
36
  def __init__(self, base_dir: str) -> None:
37
37
  super().__init__()
38
38
  self._base_dir = base_dir
@@ -2,7 +2,7 @@ import os.path
2
2
 
3
3
  from omlish import lang
4
4
 
5
- from .cache import DataCache
5
+ from .cache import Cache
6
6
 
7
7
 
8
8
  ##
@@ -14,5 +14,5 @@ def default_dir() -> str:
14
14
 
15
15
 
16
16
  @lang.cached_function(lock=True)
17
- def default() -> DataCache:
18
- return DataCache(default_dir())
17
+ def default() -> Cache:
18
+ return Cache(default_dir())
@@ -0,0 +1,37 @@
1
+ import abc
2
+ import dataclasses as dc
3
+ import typing as ta
4
+
5
+
6
+ PrecheckConfigT = ta.TypeVar('PrecheckConfigT', bound='Precheck.Config')
7
+
8
+
9
+ ##
10
+
11
+
12
+ @dc.dataclass(frozen=True, kw_only=True)
13
+ class PrecheckContext:
14
+ src_roots: ta.Sequence[str]
15
+
16
+
17
+ ##
18
+
19
+
20
+ class Precheck(abc.ABC, ta.Generic[PrecheckConfigT]):
21
+ @dc.dataclass(frozen=True)
22
+ class Config:
23
+ pass
24
+
25
+ def __init__(self, context: PrecheckContext, config: PrecheckConfigT) -> None:
26
+ super().__init__()
27
+ self._context = context
28
+ self._config = config
29
+
30
+ @dc.dataclass(frozen=True)
31
+ class Violation:
32
+ pc: 'Precheck'
33
+ msg: str
34
+
35
+ @abc.abstractmethod
36
+ def run(self) -> ta.AsyncIterator[Violation]:
37
+ raise NotImplementedError
@@ -0,0 +1,34 @@
1
+ import asyncio
2
+ import dataclasses as dc
3
+ import typing as ta
4
+
5
+ from .base import Precheck
6
+ from .base import PrecheckContext
7
+
8
+
9
+ ##
10
+
11
+
12
+ class GitBlacklistPrecheck(Precheck['GitBlacklistPrecheck.Config']):
13
+ """
14
+ TODO:
15
+ - globs
16
+ - regex
17
+ """
18
+
19
+ @dc.dataclass(frozen=True)
20
+ class Config(Precheck.Config):
21
+ files: ta.Sequence[str] = (
22
+ '.env',
23
+ 'secrets.yml',
24
+ )
25
+
26
+ def __init__(self, context: PrecheckContext, config: Config = Config()) -> None:
27
+ super().__init__(context, config)
28
+
29
+ async def run(self) -> ta.AsyncGenerator[Precheck.Violation, None]:
30
+ for f in self._config.files:
31
+ proc = await asyncio.create_subprocess_exec('git', 'status', '-s', f)
32
+ await proc.communicate()
33
+ if proc.returncode:
34
+ yield Precheck.Violation(self, f)
@@ -0,0 +1,135 @@
1
+ import asyncio
2
+ import dataclasses as dc
3
+ import glob
4
+ import inspect
5
+ import logging
6
+ import os.path
7
+ import subprocess
8
+ import textwrap
9
+ import typing as ta
10
+
11
+ from omdev import findmagic
12
+ from omlish import cached
13
+
14
+ from .base import Precheck
15
+ from .base import PrecheckContext
16
+
17
+
18
+ log = logging.getLogger(__name__)
19
+
20
+
21
+ ##
22
+
23
+
24
+ class LitePython8Precheck(Precheck['LitePython8Precheck.Config']):
25
+ @dc.dataclass(frozen=True)
26
+ class Config(Precheck.Config):
27
+ pass
28
+
29
+ def __init__(self, context: PrecheckContext, config: Config = Config()) -> None:
30
+ super().__init__(context, config)
31
+
32
+ #
33
+
34
+ @staticmethod
35
+ def _load_file_module(fp: str) -> None:
36
+ import os.path # noqa
37
+ import types # noqa
38
+
39
+ fp = os.path.abspath(fp)
40
+
41
+ with open(fp) as f:
42
+ src = f.read()
43
+
44
+ mn = os.path.basename(fp).rpartition('.')[0]
45
+
46
+ mod = types.ModuleType(mn)
47
+ mod.__name__ = mn
48
+ mod.__file__ = fp
49
+ mod.__builtins__ = __builtins__ # type: ignore
50
+ mod.__spec__ = None
51
+
52
+ code = compile(src, fp, 'exec')
53
+ exec(code, mod.__dict__, mod.__dict__)
54
+
55
+ @cached.function
56
+ def _load_file_module_payload(self) -> str:
57
+ return '\n'.join([
58
+ 'import sys',
59
+ 'fp = sys.argv[-1]',
60
+ '',
61
+ textwrap.dedent('\n'.join(inspect.getsource(LitePython8Precheck._load_file_module).splitlines()[2:])),
62
+ ])
63
+
64
+ #
65
+
66
+ async def _run_script(self, fp: str) -> list[Precheck.Violation]:
67
+ log.debug('%s: loading script %s', self.__class__.__name__, fp)
68
+
69
+ vs: list[Precheck.Violation] = []
70
+
71
+ proc = await asyncio.create_subprocess_exec(
72
+ '.venvs/8/bin/python',
73
+ '-c',
74
+ self._load_file_module_payload(),
75
+ fp,
76
+ stderr=subprocess.PIPE,
77
+ )
78
+
79
+ _, stderr = await proc.communicate()
80
+ if proc.returncode != 0:
81
+ vs.append(Precheck.Violation(self, f'lite script {fp} failed to load in python8: {stderr.decode()}'))
82
+
83
+ return vs
84
+
85
+ async def _run_one_module(self, fp: str) -> list[Precheck.Violation]:
86
+ vs: list[Precheck.Violation] = []
87
+
88
+ mod = fp.rpartition('.')[0].replace(os.sep, '.')
89
+
90
+ log.debug('%s: loading module %s', self.__class__.__name__, mod)
91
+
92
+ proc = await asyncio.create_subprocess_exec(
93
+ '.venvs/8/bin/python',
94
+ '-c',
95
+ f'import {mod}',
96
+ stderr=subprocess.PIPE,
97
+ )
98
+
99
+ _, stderr = await proc.communicate()
100
+ if proc.returncode != 0:
101
+ vs.append(Precheck.Violation(self, f'lite module {fp} failed to import in python8: {stderr.decode()}')) # noqa
102
+
103
+ return vs
104
+
105
+ async def _run_module(self, fp: str) -> list[Precheck.Violation]:
106
+ vs: list[Precheck.Violation] = []
107
+
108
+ if fp.endswith('__init__.py'):
109
+ pfps = glob.glob(os.path.join(os.path.dirname(fp), '**/*.py'), recursive=True)
110
+ else:
111
+ pfps = [fp]
112
+
113
+ for pfp in pfps:
114
+ vs.extend(await self._run_one_module(pfp))
115
+
116
+ return vs
117
+
118
+ async def run(self) -> ta.AsyncGenerator[Precheck.Violation, None]:
119
+ for fp in findmagic.find_magic(
120
+ self._context.src_roots,
121
+ ['# @omlish-lite'],
122
+ ['py'],
123
+ ):
124
+ with open(fp) as f: # noqa # FIXME
125
+ src = f.read()
126
+
127
+ is_script = '# @omlish-script' in src.splitlines()
128
+
129
+ if is_script:
130
+ for v in await self._run_script(fp):
131
+ yield v
132
+
133
+ else:
134
+ for v in await self._run_module(fp):
135
+ yield v
@@ -0,0 +1,100 @@
1
+ """
2
+ Tiny pre-commit
3
+
4
+ TODO:
5
+ - global config
6
+ - global analyses - FilesWithShebang
7
+ - shebang files have no relative imports
8
+ - parallelize (asyncio)
9
+ - anyio? aiofiles? :| nonblock open().read()
10
+ - debug log
11
+ - omlish-lite - no non-lite deps, etc etc
12
+ - omlish-script - no deps, shebang, executable, can be 3.12
13
+ - big git files https://github.com/pre-commit/pre-commit-hooks?tab=readme-ov-file#check-added-large-files
14
+ - https://github.com/pre-commit/pre-commit-hooks?tab=readme-ov-file#check-case-conflict
15
+ - https://github.com/pre-commit/pre-commit-hooks?tab=readme-ov-file#check-symlinks
16
+ - https://github.com/pre-commit/pre-commit-hooks?tab=readme-ov-file#detect-aws-credentials
17
+ - https://github.com/pre-commit/pre-commit-hooks?tab=readme-ov-file#forbid-new-submodules
18
+ - don't check in .o's (omdev.ext import hook is dumb w build dir)
19
+ """
20
+ import argparse
21
+ import asyncio
22
+ import logging
23
+ import os.path
24
+ import sys
25
+ import typing as ta
26
+
27
+ from omlish import logs
28
+
29
+ from .base import Precheck
30
+ from .base import PrecheckContext
31
+ from .git import GitBlacklistPrecheck
32
+ from .lite import LitePython8Precheck
33
+ from .scripts import ScriptDepsPrecheck
34
+
35
+
36
+ log = logging.getLogger(__name__)
37
+
38
+
39
+ ##
40
+
41
+
42
+ def _check_cmd(args) -> None:
43
+ if not os.path.isfile('pyproject.toml'):
44
+ raise RuntimeError('must run in project root')
45
+
46
+ ctx = PrecheckContext(
47
+ src_roots=args.roots,
48
+ )
49
+
50
+ pcs: list[Precheck] = [
51
+ GitBlacklistPrecheck(ctx),
52
+ ScriptDepsPrecheck(ctx),
53
+ LitePython8Precheck(ctx),
54
+ ]
55
+
56
+ async def run() -> list[Precheck.Violation]:
57
+ vs: list[Precheck.Violation] = []
58
+
59
+ for pc in pcs:
60
+ async for v in pc.run():
61
+ vs.append(v)
62
+ print(v)
63
+
64
+ return vs
65
+
66
+ vs = asyncio.run(run())
67
+
68
+ if vs:
69
+ print(f'{len(vs)} violations found')
70
+ sys.exit(1)
71
+
72
+
73
+ ##
74
+
75
+
76
+ def _build_parser() -> argparse.ArgumentParser:
77
+ parser = argparse.ArgumentParser()
78
+
79
+ subparsers = parser.add_subparsers()
80
+
81
+ parser_check = subparsers.add_parser('check')
82
+ parser_check.add_argument('roots', nargs='+')
83
+ parser_check.set_defaults(func=_check_cmd)
84
+
85
+ return parser
86
+
87
+
88
+ def _main(argv: ta.Sequence[str] | None = None) -> None:
89
+ logs.configure_standard_logging('INFO')
90
+
91
+ parser = _build_parser()
92
+ args = parser.parse_args(argv)
93
+ if not getattr(args, 'func', None):
94
+ parser.print_help()
95
+ else:
96
+ args.func(args)
97
+
98
+
99
+ if __name__ == '__main__':
100
+ _main()
@@ -0,0 +1,42 @@
1
+ import dataclasses as dc
2
+ import os
3
+ import stat
4
+ import typing as ta
5
+
6
+ from omdev import findimports
7
+ from omdev import findmagic
8
+
9
+ from .base import Precheck
10
+ from .base import PrecheckContext
11
+
12
+
13
+ ##
14
+
15
+
16
+ class ScriptDepsPrecheck(Precheck['ScriptDepsPrecheck.Config']):
17
+ @dc.dataclass(frozen=True)
18
+ class Config(Precheck.Config):
19
+ pass
20
+
21
+ def __init__(self, context: PrecheckContext, config: Config = Config()) -> None:
22
+ super().__init__(context, config)
23
+
24
+ async def run(self) -> ta.AsyncGenerator[Precheck.Violation, None]:
25
+ for fp in findmagic.find_magic(
26
+ self._context.src_roots,
27
+ ['# @omlish-script'],
28
+ ['py'],
29
+ ):
30
+ if not (stat.S_IXUSR & os.stat(fp).st_mode):
31
+ yield Precheck.Violation(self, f'script {fp} is not executable')
32
+
33
+ with open(fp) as f: # noqa # FIXME
34
+ src = f.read()
35
+
36
+ if not src.startswith('#!/usr/bin/env python3\n'):
37
+ yield Precheck.Violation(self, f'script {fp} lacks correct shebang')
38
+
39
+ imps = findimports.find_imports(fp)
40
+ deps = findimports.get_import_deps(imps)
41
+ if deps:
42
+ yield Precheck.Violation(self, f'script {fp} has deps: {deps}')
@@ -0,0 +1,26 @@
1
+ import io
2
+ import urllib.request
3
+ import xml.etree.ElementTree as ET # noqa
4
+
5
+ from omlish import argparse as ap
6
+ from omlish import check
7
+
8
+
9
+ PYPI_URL = 'https://pypi.org/'
10
+
11
+
12
+ class Cli(ap.Cli):
13
+ @ap.command(
14
+ ap.arg('package'),
15
+ )
16
+ def lookup_latest_version(self) -> None:
17
+ pkg_name = check.non_empty_str(self.args.package)
18
+ with urllib.request.urlopen(f'{PYPI_URL}rss/project/{pkg_name}/releases.xml') as resp: # noqa
19
+ rss = resp.read()
20
+ doc = ET.parse(io.BytesIO(rss)) # noqa
21
+ latest = check.not_none(doc.find('./channel/item/title')).text
22
+ print(latest)
23
+
24
+
25
+ if __name__ == '__main__':
26
+ Cli()()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev28
3
+ Version: 0.0.0.dev29
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.dev28
15
+ Requires-Dist: omlish==0.0.0.dev29
16
16
  Provides-Extra: all
17
17
  Requires-Dist: pycparser~=2.22; extra == "all"
18
18
  Requires-Dist: cffi~=1.17; extra == "all"
@@ -74,7 +74,11 @@ omdev/mypy/__init__.py
74
74
  omdev/mypy/debug.py
75
75
  omdev/precheck/__init__.py
76
76
  omdev/precheck/__main__.py
77
+ omdev/precheck/base.py
78
+ omdev/precheck/git.py
79
+ omdev/precheck/lite.py
77
80
  omdev/precheck/precheck.py
81
+ omdev/precheck/scripts.py
78
82
  omdev/pyproject/__init__.py
79
83
  omdev/pyproject/__main__.py
80
84
  omdev/pyproject/cexts.py
@@ -95,6 +99,7 @@ omdev/tools/dockertools.py
95
99
  omdev/tools/gittools.py
96
100
  omdev/tools/importscan.py
97
101
  omdev/tools/importtrace.py
102
+ omdev/tools/piptools.py
98
103
  omdev/tools/rst.py
99
104
  omdev/tools/sqlrepl.py
100
105
  omdev/versioning/__init__.py
@@ -1,4 +1,4 @@
1
- omlish==0.0.0.dev28
1
+ omlish==0.0.0.dev29
2
2
 
3
3
  [all]
4
4
  pycparser~=2.22
@@ -12,7 +12,7 @@ authors = [
12
12
  urls = {source = 'https://github.com/wrmsr/omlish'}
13
13
  license = {text = 'BSD-3-Clause'}
14
14
  requires-python = '~=3.12'
15
- version = '0.0.0.dev28'
15
+ version = '0.0.0.dev29'
16
16
  classifiers = [
17
17
  'License :: OSI Approved :: BSD License',
18
18
  'Development Status :: 2 - Pre-Alpha',
@@ -22,7 +22,7 @@ classifiers = [
22
22
  ]
23
23
  description = 'omdev'
24
24
  dependencies = [
25
- 'omlish == 0.0.0.dev28',
25
+ 'omlish == 0.0.0.dev29',
26
26
  ]
27
27
 
28
28
  [project.optional-dependencies]
@@ -1,316 +0,0 @@
1
- """
2
- Tiny pre-commit
3
-
4
- TODO:
5
- - global config
6
- - global analyses - FilesWithShebang
7
- - shebang files have no relative imports
8
- - parallelize (asyncio)
9
- - anyio? aiofiles? :| nonblock open().read()
10
- - debug log
11
- - omlish-lite - no non-lite deps, etc etc
12
- - omlish-script - no deps, shebang, executable, can be 3.12
13
- - big git files https://github.com/pre-commit/pre-commit-hooks?tab=readme-ov-file#check-added-large-files
14
- - https://github.com/pre-commit/pre-commit-hooks?tab=readme-ov-file#check-case-conflict
15
- - https://github.com/pre-commit/pre-commit-hooks?tab=readme-ov-file#check-symlinks
16
- - https://github.com/pre-commit/pre-commit-hooks?tab=readme-ov-file#detect-aws-credentials
17
- - https://github.com/pre-commit/pre-commit-hooks?tab=readme-ov-file#forbid-new-submodules
18
- - don't check in .o's (omdev.ext import hook is dumb w build dir)
19
- """
20
- import abc
21
- import argparse
22
- import asyncio
23
- import dataclasses as dc
24
- import glob
25
- import inspect
26
- import logging
27
- import os.path
28
- import stat
29
- import subprocess
30
- import sys
31
- import textwrap
32
- import typing as ta
33
-
34
- from omdev import findimports
35
- from omdev import findmagic
36
- from omlish import cached
37
- from omlish import logs
38
-
39
-
40
- T = ta.TypeVar('T')
41
- PrecheckConfigT = ta.TypeVar('PrecheckConfigT', bound='Precheck.Config')
42
-
43
-
44
- log = logging.getLogger(__name__)
45
-
46
-
47
- ##
48
-
49
-
50
- @dc.dataclass(frozen=True, kw_only=True)
51
- class PrecheckContext:
52
- src_roots: ta.Sequence[str]
53
-
54
-
55
- ##
56
-
57
-
58
- class Precheck(abc.ABC, ta.Generic[PrecheckConfigT]):
59
- @dc.dataclass(frozen=True)
60
- class Config:
61
- pass
62
-
63
- def __init__(self, context: PrecheckContext, config: PrecheckConfigT) -> None:
64
- super().__init__()
65
- self._context = context
66
- self._config = config
67
-
68
- @dc.dataclass(frozen=True)
69
- class Violation:
70
- pc: 'Precheck'
71
- msg: str
72
-
73
- @abc.abstractmethod
74
- def run(self) -> ta.AsyncIterator[Violation]:
75
- raise NotImplementedError
76
-
77
-
78
- ##
79
-
80
-
81
- class GitBlacklistPrecheck(Precheck['GitBlacklistPrecheck.Config']):
82
- """
83
- TODO:
84
- - globs
85
- - regex
86
- """
87
-
88
- @dc.dataclass(frozen=True)
89
- class Config(Precheck.Config):
90
- files: ta.Sequence[str] = (
91
- '.env',
92
- 'secrets.yml',
93
- )
94
-
95
- def __init__(self, context: PrecheckContext, config: Config = Config()) -> None:
96
- super().__init__(context, config)
97
-
98
- async def run(self) -> ta.AsyncGenerator[Precheck.Violation, None]:
99
- for f in self._config.files:
100
- proc = await asyncio.create_subprocess_exec('git', 'status', '-s', f)
101
- await proc.communicate()
102
- if proc.returncode:
103
- yield Precheck.Violation(self, f)
104
-
105
-
106
- ##
107
-
108
-
109
- class ScriptDepsPrecheck(Precheck['ScriptDepsPrecheck.Config']):
110
- @dc.dataclass(frozen=True)
111
- class Config(Precheck.Config):
112
- pass
113
-
114
- def __init__(self, context: PrecheckContext, config: Config = Config()) -> None:
115
- super().__init__(context, config)
116
-
117
- async def run(self) -> ta.AsyncGenerator[Precheck.Violation, None]:
118
- for fp in findmagic.find_magic(
119
- self._context.src_roots,
120
- ['# @omlish-script'],
121
- ['py'],
122
- ):
123
- if not (stat.S_IXUSR & os.stat(fp).st_mode):
124
- yield Precheck.Violation(self, f'script {fp} is not executable')
125
-
126
- with open(fp) as f: # noqa # FIXME
127
- src = f.read()
128
-
129
- if not src.startswith('#!/usr/bin/env python3\n'):
130
- yield Precheck.Violation(self, f'script {fp} lacks correct shebang')
131
-
132
- imps = findimports.find_imports(fp)
133
- deps = findimports.get_import_deps(imps)
134
- if deps:
135
- yield Precheck.Violation(self, f'script {fp} has deps: {deps}')
136
-
137
-
138
- ##
139
-
140
-
141
- class LitePython8Precheck(Precheck['LitePython8Precheck.Config']):
142
- @dc.dataclass(frozen=True)
143
- class Config(Precheck.Config):
144
- pass
145
-
146
- def __init__(self, context: PrecheckContext, config: Config = Config()) -> None:
147
- super().__init__(context, config)
148
-
149
- #
150
-
151
- @staticmethod
152
- def _load_file_module(fp: str) -> None:
153
- import os.path # noqa
154
- import types # noqa
155
-
156
- fp = os.path.abspath(fp)
157
-
158
- with open(fp) as f:
159
- src = f.read()
160
-
161
- mn = os.path.basename(fp).rpartition('.')[0]
162
-
163
- mod = types.ModuleType(mn)
164
- mod.__name__ = mn
165
- mod.__file__ = fp
166
- mod.__builtins__ = __builtins__ # type: ignore
167
- mod.__spec__ = None
168
-
169
- code = compile(src, fp, 'exec')
170
- exec(code, mod.__dict__, mod.__dict__)
171
-
172
- @cached.function
173
- def _load_file_module_payload(self) -> str:
174
- return '\n'.join([
175
- 'import sys',
176
- 'fp = sys.argv[-1]',
177
- '',
178
- textwrap.dedent('\n'.join(inspect.getsource(LitePython8Precheck._load_file_module).splitlines()[2:])),
179
- ])
180
-
181
- #
182
-
183
- async def _run_script(self, fp: str) -> list[Precheck.Violation]:
184
- log.debug('%s: loading script %s', self.__class__.__name__, fp)
185
-
186
- vs: list[Precheck.Violation] = []
187
-
188
- proc = await asyncio.create_subprocess_exec(
189
- '.venvs/8/bin/python',
190
- '-c',
191
- self._load_file_module_payload(),
192
- fp,
193
- stderr=subprocess.PIPE,
194
- )
195
-
196
- _, stderr = await proc.communicate()
197
- if proc.returncode != 0:
198
- vs.append(Precheck.Violation(self, f'lite script {fp} failed to load in python8: {stderr.decode()}'))
199
-
200
- return vs
201
-
202
- async def _run_one_module(self, fp: str) -> list[Precheck.Violation]:
203
- vs: list[Precheck.Violation] = []
204
-
205
- mod = fp.rpartition('.')[0].replace(os.sep, '.')
206
-
207
- log.debug('%s: loading module %s', self.__class__.__name__, mod)
208
-
209
- proc = await asyncio.create_subprocess_exec(
210
- '.venvs/8/bin/python',
211
- '-c',
212
- f'import {mod}',
213
- stderr=subprocess.PIPE,
214
- )
215
-
216
- _, stderr = await proc.communicate()
217
- if proc.returncode != 0:
218
- vs.append(Precheck.Violation(self, f'lite module {fp} failed to import in python8: {stderr.decode()}')) # noqa
219
-
220
- return vs
221
-
222
- async def _run_module(self, fp: str) -> list[Precheck.Violation]:
223
- vs: list[Precheck.Violation] = []
224
-
225
- if fp.endswith('__init__.py'):
226
- pfps = glob.glob(os.path.join(os.path.dirname(fp), '**/*.py'), recursive=True)
227
- else:
228
- pfps = [fp]
229
-
230
- for pfp in pfps:
231
- vs.extend(await self._run_one_module(pfp))
232
-
233
- return vs
234
-
235
- async def run(self) -> ta.AsyncGenerator[Precheck.Violation, None]:
236
- for fp in findmagic.find_magic(
237
- self._context.src_roots,
238
- ['# @omlish-lite'],
239
- ['py'],
240
- ):
241
- with open(fp) as f: # noqa # FIXME
242
- src = f.read()
243
-
244
- is_script = '# @omlish-script' in src.splitlines()
245
-
246
- if is_script:
247
- for v in await self._run_script(fp):
248
- yield v
249
-
250
- else:
251
- for v in await self._run_module(fp):
252
- yield v
253
-
254
-
255
- ##
256
-
257
-
258
- def _check_cmd(args) -> None:
259
- if not os.path.isfile('pyproject.toml'):
260
- raise RuntimeError('must run in project root')
261
-
262
- ctx = PrecheckContext(
263
- src_roots=args.roots,
264
- )
265
-
266
- pcs: list[Precheck] = [
267
- GitBlacklistPrecheck(ctx),
268
- ScriptDepsPrecheck(ctx),
269
- LitePython8Precheck(ctx),
270
- ]
271
-
272
- async def run() -> list[Precheck.Violation]:
273
- vs: list[Precheck.Violation] = []
274
-
275
- for pc in pcs:
276
- async for v in pc.run():
277
- vs.append(v)
278
- print(v)
279
-
280
- return vs
281
-
282
- vs = asyncio.run(run())
283
-
284
- if vs:
285
- print(f'{len(vs)} violations found')
286
- sys.exit(1)
287
-
288
-
289
- ##
290
-
291
-
292
- def _build_parser() -> argparse.ArgumentParser:
293
- parser = argparse.ArgumentParser()
294
-
295
- subparsers = parser.add_subparsers()
296
-
297
- parser_check = subparsers.add_parser('check')
298
- parser_check.add_argument('roots', nargs='+')
299
- parser_check.set_defaults(func=_check_cmd)
300
-
301
- return parser
302
-
303
-
304
- def _main(argv: ta.Sequence[str] | None = None) -> None:
305
- logs.configure_standard_logging('INFO')
306
-
307
- parser = _build_parser()
308
- args = parser.parse_args(argv)
309
- if not getattr(args, 'func', None):
310
- parser.print_help()
311
- else:
312
- args.func(args)
313
-
314
-
315
- if __name__ == '__main__':
316
- _main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes