omdev 0.0.0.dev163__py3-none-any.whl → 0.0.0.dev165__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.

@@ -0,0 +1,120 @@
1
+ import dataclasses as dc
2
+ import typing as ta
3
+
4
+ import tokenize_rt as trt
5
+
6
+ from omlish import collections as col
7
+ from omlish import lang
8
+
9
+ from .. import tokens as tks
10
+ from .imports import Import
11
+ from .imports import make_import
12
+ from .manifests import comment_out_manifest_comment
13
+ from .manifests import is_manifest_comment
14
+ from .resources import build_resource_lines
15
+ from .resources import is_root_level_resources_read
16
+ from .strip import split_header_lines
17
+ from .strip import strip_header_lines
18
+ from .types import Tokens
19
+ from .typing import Typing
20
+ from .typing import is_root_level_if_type_checking_block
21
+ from .typing import make_typing
22
+ from .typing import skip_root_level_if_type_checking_block
23
+
24
+
25
+ ##
26
+
27
+
28
+ @dc.dataclass(frozen=True, kw_only=True)
29
+ class SrcFile:
30
+ path: str
31
+
32
+ src: str = dc.field(repr=False)
33
+ tokens: Tokens = dc.field(repr=False)
34
+ lines: ta.Sequence[Tokens] = dc.field(repr=False)
35
+
36
+ header_lines: ta.Sequence[Tokens] = dc.field(repr=False)
37
+ imports: ta.Sequence[Import] = dc.field(repr=False)
38
+ typings: ta.Sequence[Typing] = dc.field(repr=False)
39
+ content_lines: ta.Sequence[Tokens] = dc.field(repr=False)
40
+
41
+ ruff_noqa: ta.AbstractSet[str] = dc.field(repr=False)
42
+
43
+ has_binary_resources: bool = False
44
+
45
+
46
+ def make_src_file(
47
+ path: str,
48
+ *,
49
+ mounts: ta.Mapping[str, str],
50
+ ) -> SrcFile:
51
+ with open(path) as f:
52
+ src = f.read().strip()
53
+
54
+ tokens = trt.src_to_tokens(src)
55
+ lines = tks.split_lines(tokens)
56
+
57
+ header_lines, cls = split_header_lines(lines)
58
+
59
+ header_lines = strip_header_lines(header_lines)
60
+ rnls, header_lines = col.partition(header_lines, lambda l: tks.join_toks(l).startswith('# ruff: noqa: '))
61
+
62
+ imps: list[Import] = []
63
+ tys: list[Typing] = []
64
+ ctls: list[Tokens] = []
65
+
66
+ has_binary_resources = False
67
+
68
+ i = 0
69
+ while i < len(cls):
70
+ line = cls[i]
71
+ i += 1
72
+
73
+ if (imp := make_import(
74
+ line,
75
+ src_path=path,
76
+ mounts=mounts,
77
+ )) is not None:
78
+ imps.append(imp)
79
+
80
+ elif (ty := make_typing(
81
+ line,
82
+ src_path=path,
83
+ )) is not None:
84
+ tys.append(ty)
85
+
86
+ elif is_manifest_comment(line):
87
+ out, i = comment_out_manifest_comment(line, cls, i)
88
+ ctls.extend(out)
89
+
90
+ elif is_root_level_if_type_checking_block(line):
91
+ i = skip_root_level_if_type_checking_block(cls, i)
92
+
93
+ elif (rsrc := is_root_level_resources_read(line)) is not None:
94
+ ctls.extend(build_resource_lines(
95
+ rsrc,
96
+ path,
97
+ ))
98
+
99
+ if rsrc.kind == 'binary':
100
+ has_binary_resources = True
101
+
102
+ else:
103
+ ctls.append(line)
104
+
105
+ return SrcFile(
106
+ path=path,
107
+
108
+ src=src,
109
+ tokens=tokens,
110
+ lines=lines,
111
+
112
+ header_lines=header_lines,
113
+ imports=imps,
114
+ typings=tys,
115
+ content_lines=ctls,
116
+
117
+ ruff_noqa=set(lang.flatten(tks.join_toks(l).strip().split()[3:] for l in rnls)), # noqa
118
+
119
+ has_binary_resources=has_binary_resources,
120
+ )
omdev/amalg/strip.py ADDED
@@ -0,0 +1,70 @@
1
+ import re
2
+ import typing as ta
3
+
4
+ from .. import magic
5
+ from .. import tokens as tks
6
+ from .types import Tokens
7
+
8
+
9
+ ##
10
+
11
+
12
+ HEADER_NAMES = (*tks.WS_NAMES, 'COMMENT', 'STRING')
13
+
14
+
15
+ def split_header_lines(lines: ta.Iterable[Tokens]) -> tuple[list[Tokens], list[Tokens]]:
16
+ ws = []
17
+ nws = []
18
+ for line in (it := iter(lines)):
19
+ if line[0].name in HEADER_NAMES:
20
+ ws.append(line)
21
+ else:
22
+ nws.append(line)
23
+ nws.extend(it)
24
+ break
25
+ return ws, nws
26
+
27
+
28
+ ##
29
+
30
+
31
+ IF_MAIN_PAT = re.compile(r'if\s+__name__\s+==\s+[\'"]__main__[\'"]\s*:')
32
+
33
+
34
+ def strip_main_lines(cls: ta.Sequence[Tokens]) -> list[Tokens]:
35
+ out = []
36
+
37
+ for l in (it := iter(cls)):
38
+ if IF_MAIN_PAT.fullmatch(tks.join_toks(l).strip()):
39
+ for l in it:
40
+ if l[0].name not in ('INDENT', 'UNIMPORTANT_WS') and tks.join_toks(l).strip():
41
+ break
42
+ else:
43
+ out.append(l)
44
+
45
+ return out
46
+
47
+
48
+ ##
49
+
50
+
51
+ STRIPPED_HEADER_MAGICS = [
52
+ '@omlish-lite',
53
+ '@omlish-script',
54
+ ]
55
+
56
+ STRIPPED_HEADER_PAT = magic.compile_magic_style_pat(
57
+ magic.PY_MAGIC_STYLE,
58
+ keys=STRIPPED_HEADER_MAGICS,
59
+ )
60
+
61
+
62
+ def strip_header_lines(hls: ta.Sequence[Tokens]) -> list[Tokens]:
63
+ if hls and tks.join_toks(hls[0]).startswith('#!'):
64
+ hls = hls[1:]
65
+ out = []
66
+ for l in hls:
67
+ ls = tks.join_toks(l)
68
+ if not STRIPPED_HEADER_PAT.fullmatch(ls):
69
+ out.append(l)
70
+ return out
omdev/amalg/types.py ADDED
@@ -0,0 +1,6 @@
1
+ import typing as ta
2
+
3
+ from .. import tokens as tks
4
+
5
+
6
+ Tokens: ta.TypeAlias = tks.Tokens
omdev/amalg/typing.py ADDED
@@ -0,0 +1,106 @@
1
+ import dataclasses as dc
2
+
3
+ from .. import tokens as tks
4
+ from .types import Tokens
5
+
6
+
7
+ ##
8
+
9
+
10
+ TYPE_ALIAS_COMMENT = '# ta.TypeAlias'
11
+ NOQA_TYPE_ALIAS_COMMENT = TYPE_ALIAS_COMMENT + ' # noqa'
12
+
13
+
14
+ @dc.dataclass(frozen=True, kw_only=True)
15
+ class Typing:
16
+ src: str
17
+
18
+ src_path: str
19
+ line: int
20
+
21
+ toks: Tokens = dc.field(repr=False)
22
+
23
+
24
+ def _is_typing(
25
+ lts: Tokens,
26
+ *,
27
+ exclude_newtypes: bool = False,
28
+ ) -> bool:
29
+ es = tks.join_toks(lts).strip()
30
+ if any(es.endswith(sfx) for sfx in (TYPE_ALIAS_COMMENT, NOQA_TYPE_ALIAS_COMMENT)):
31
+ return True
32
+
33
+ wts = list(tks.ignore_ws(lts, keep=['INDENT', 'UNINDENT']))
34
+ if not tks.match_toks(wts, [
35
+ ('NAME', None),
36
+ ('OP', '='),
37
+ ('NAME', 'ta'),
38
+ ('OP', '.'),
39
+ (None, None),
40
+ ]):
41
+ return False
42
+
43
+ if exclude_newtypes:
44
+ if wts[4].name == 'NAME' and wts[4].src == 'NewType':
45
+ return False
46
+
47
+ return True
48
+
49
+
50
+ def make_typing(
51
+ lts: Tokens,
52
+ *,
53
+ src_path: str,
54
+ ) -> Typing | None:
55
+ if not lts or lts[0].name == 'UNIMPORTANT_WS':
56
+ return None
57
+
58
+ if not _is_typing(lts, exclude_newtypes=True):
59
+ return None
60
+
61
+ ft = next(iter(tks.ignore_ws(lts)))
62
+ return Typing(
63
+ src=tks.join_toks(lts),
64
+
65
+ src_path=src_path,
66
+ line=ft.line,
67
+
68
+ toks=lts,
69
+ )
70
+
71
+
72
+ ##
73
+
74
+
75
+ def is_root_level_if_type_checking_block(lts: Tokens) -> bool:
76
+ return tks.match_toks(tks.ignore_ws(lts, keep=['INDENT']), [
77
+ ('NAME', 'if'),
78
+ ('NAME', 'ta'),
79
+ ('OP', '.'),
80
+ ('NAME', 'TYPE_CHECKING'),
81
+ ('OP', ':'),
82
+ ])
83
+
84
+
85
+ def skip_root_level_if_type_checking_block(
86
+ cls: list[Tokens],
87
+ i: int,
88
+ ) -> int:
89
+ def skip_block():
90
+ nonlocal i
91
+ while True:
92
+ nl = cls[i]
93
+ if nl and nl[0].name != 'INDENT':
94
+ return nl
95
+ i += 1
96
+
97
+ nl = skip_block()
98
+
99
+ if tks.match_toks(nl, [
100
+ ('DEDENT', None),
101
+ ('NAME', 'else'),
102
+ ]):
103
+ i += 1
104
+ skip_block()
105
+
106
+ return i
omdev/tools/cloc.py CHANGED
@@ -71,15 +71,29 @@ def count_lines(file_path: str, language: str) -> FileLineCount:
71
71
  )
72
72
 
73
73
 
74
- def count_lines_in_directory(directory: str) -> ta.Mapping[str, FileLineCount]:
74
+ def count_lines_in_directory(
75
+ directory: str,
76
+ *,
77
+ include: list[re.Pattern[str]] | None = None,
78
+ exclude: list[re.Pattern[str]] | None = None,
79
+ ) -> ta.Mapping[str, FileLineCount]:
75
80
  results: dict[str, FileLineCount] = {}
76
81
  for root, _, files in os.walk(directory):
77
82
  for file in files:
78
83
  ext = os.path.splitext(file)[1]
79
- if ext in SUPPORTED_EXTENSIONS:
80
- language = SUPPORTED_EXTENSIONS[ext]
81
- file_path = os.path.join(root, file)
82
- results[file_path] = count_lines(file_path, language)
84
+ if ext not in SUPPORTED_EXTENSIONS:
85
+ continue
86
+
87
+ file_path = os.path.join(root, file)
88
+
89
+ if include and not any(p.fullmatch(file_path) for p in include):
90
+ continue
91
+ if exclude and any(p.fullmatch(file_path) for p in exclude):
92
+ continue
93
+
94
+ language = SUPPORTED_EXTENSIONS[ext]
95
+ results[file_path] = count_lines(file_path, language)
96
+
83
97
  return results
84
98
 
85
99
 
@@ -132,14 +146,50 @@ def _main() -> None:
132
146
  import argparse
133
147
 
134
148
  parser = argparse.ArgumentParser(description='Count lines of code in source files.')
149
+
135
150
  parser.add_argument('directory', help='The directory to analyze.', nargs='+')
151
+
152
+ parser.add_argument('-i,', '--include', action='append')
153
+ parser.add_argument('-e,', '--exclude', action='append')
154
+
136
155
  args = parser.parse_args()
137
156
 
157
+ #
158
+
159
+ include: list[re.Pattern[str]] | None = None
160
+ if args.include:
161
+ include = [re.compile(p) for p in args.include]
162
+
163
+ exclude: list[re.Pattern[str]] | None = None
164
+ if args.exclude:
165
+ exclude = [re.compile(p) for p in args.exclude]
166
+
167
+ #
168
+
169
+ results_by_directory: dict[str, FileLineCount] = {}
138
170
  for directory in args.directory:
139
- results = count_lines_in_directory(directory)
171
+ results = count_lines_in_directory(
172
+ directory,
173
+ include=include,
174
+ exclude=exclude,
175
+ )
176
+
177
+ if not results:
178
+ continue
179
+
140
180
  display_results(results)
141
181
  print()
142
182
 
183
+ results_by_directory[directory] = FileLineCount(
184
+ loc=sum(flc.loc for flc in results.values()),
185
+ blanks=sum(flc.blanks for flc in results.values()),
186
+ comments=sum(flc.comments for flc in results.values()),
187
+ )
188
+
189
+ if len(results_by_directory) > 1:
190
+ display_results(results_by_directory)
191
+ print()
192
+
143
193
 
144
194
  # @omlish-manifest
145
195
  _CLI_MODULE = CliModule('cloc', __name__)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev163
3
+ Version: 0.0.0.dev165
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.dev163
15
+ Requires-Dist: omlish==0.0.0.dev165
16
16
  Provides-Extra: all
17
17
  Requires-Dist: black~=24.10; extra == "all"
18
18
  Requires-Dist: pycparser~=2.22; extra == "all"
@@ -1,4 +1,4 @@
1
- omdev/.manifests.json,sha256=Dm-Nd3V7Ry9d3-tO5OnUM7IYT2_ldruPEjxcSr4BA1U,8306
1
+ omdev/.manifests.json,sha256=tF8O0ywJseQMbbhT2b67QAvjuAaHXLY1MyjeMrDObos,8306
2
2
  omdev/__about__.py,sha256=n5x-SO70OgbDQFzQ1d7sZDVMsnkQc4PxQZPFaIQFa0E,1281
3
3
  omdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omdev/bracepy.py,sha256=I8EdqtDvxzAi3I8TuMEW-RBfwXfqKbwp06CfOdj3L1o,2743
@@ -12,8 +12,16 @@ omdev/secrets.py,sha256=bcquaBIDKqX4UIKOzUuKrX7nxVCenj67rRHIMIrd9bk,540
12
12
  omdev/tokens.py,sha256=zh2TCAfCbcq8ZnoVdQ824jrTiwNy3XJ_oCqlZpLpcCY,1574
13
13
  omdev/wheelfile.py,sha256=yfupGcGkbFlmzGzKU64k_vmOKpaKnUlDWxeGn2KdekU,10005
14
14
  omdev/amalg/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- omdev/amalg/__main__.py,sha256=h94M-VqZ3AFBU2a8zOsjeKK7RF6uINhTHl6OiGbVMgw,163
16
- omdev/amalg/amalg.py,sha256=qJ-Kllp4mdmx28nt_JTlQnuO-o4XZnTTZP7Uh448b68,19333
15
+ omdev/amalg/__main__.py,sha256=QKvvBlPJalD8Ta6ry7cxChH0lHFj78SKHMMHGUtpFYI,162
16
+ omdev/amalg/gen.py,sha256=wp5CmwMUibcaIJrvwE_gQ1kInhWWgpvRntAI0ONKsQg,5995
17
+ omdev/amalg/imports.py,sha256=KNyuu0zWW63gOSsy4cl1mPs23YVAlICbaF1yEPF147o,2072
18
+ omdev/amalg/main.py,sha256=Ce8apkFfBinNyKVfNutH6ih0tHFc6Ta7RRVyDV5cIMs,4008
19
+ omdev/amalg/manifests.py,sha256=sKnNKsS51zFFD8BIzRUVKtX50dY8pXOtYWi4WHZ_NTU,932
20
+ omdev/amalg/resources.py,sha256=8tQ2yOnGAvPd_YValZZH6_pu9wCY8QeLVBuRUcyScDw,2722
21
+ omdev/amalg/srcfiles.py,sha256=LskAq7kfpH_GuDoNOZF-sxEZiqljDN-VMXxfOfwNfks,3109
22
+ omdev/amalg/strip.py,sha256=lsIkwCle66VjlEFpeQtQ2_sKhpn4HpYN_r4KP6v0DmQ,1439
23
+ omdev/amalg/types.py,sha256=P0VKeWxXKOMQISqHcEFqSBzHQIbIly_nzi1GbMz6EZo,86
24
+ omdev/amalg/typing.py,sha256=q90Mp5JdJaNMLTTwNHSGVx-z6rgfvSZNoV2KRj4dOmg,2049
17
25
  omdev/antlr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
26
  omdev/antlr/consts.py,sha256=8pR6r0m0P3hAiyiAoJZ-nptd2GYbZ98mxwPL9cpaRuw,279
19
27
  omdev/antlr/gen.py,sha256=QnPyVWUrJYqHoOc3XsXA8fJdoiwAj6pyUwdG-DVrIeA,2953
@@ -134,7 +142,7 @@ omdev/toml/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
134
142
  omdev/toml/parser.py,sha256=ojhCYIk23ELRx2f9xUCwLTRq13UM6wrYGWoyxZBurlo,29327
135
143
  omdev/toml/writer.py,sha256=lk3on3YXVbWuLJa-xsOzOhs1bBAT1vXqw4mBbluZl_w,3040
136
144
  omdev/tools/__init__.py,sha256=iVJAOQ0viGTQOm0DLX4uZLro-9jOioYJGLg9s0kDx1A,78
137
- omdev/tools/cloc.py,sha256=13lUsYLyn1LoelhsCeKrRkIwwPE0x54JmdhXJ_lvWh0,3811
145
+ omdev/tools/cloc.py,sha256=r5HkvaLoGw8djgvGdt_W_CjfSklW585dar9bDhDFeF8,5098
138
146
  omdev/tools/doc.py,sha256=iblgUq9_7JZN2i8qmvewrz4OX0paObscBaCj8u77WqI,2555
139
147
  omdev/tools/docker.py,sha256=mu0sWnH_L1JjScfWCXXYaux03mcotCS03SD65I93qHI,7384
140
148
  omdev/tools/git.py,sha256=zfdPnN-9WSeOQlLoTw5aqAX-UWvz-2p330dx_zaU0WQ,7014
@@ -156,9 +164,9 @@ omdev/tools/json/rendering.py,sha256=jNShMfCpFR9-Kcn6cUFuOChXHjg71diuTC4x7Ofmz-o
156
164
  omdev/tools/pawk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
157
165
  omdev/tools/pawk/__main__.py,sha256=VCqeRVnqT1RPEoIrqHFSu4PXVMg4YEgF4qCQm90-eRI,66
158
166
  omdev/tools/pawk/pawk.py,sha256=Eckymn22GfychCQcQi96BFqRo_LmiJ-EPhC8TTUJdB4,11446
159
- omdev-0.0.0.dev163.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
160
- omdev-0.0.0.dev163.dist-info/METADATA,sha256=ZAnRFPdzzNWzA3fjK-vWz97_cfD2z1VaBr1Jp1qNeqc,1760
161
- omdev-0.0.0.dev163.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
162
- omdev-0.0.0.dev163.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
163
- omdev-0.0.0.dev163.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
164
- omdev-0.0.0.dev163.dist-info/RECORD,,
167
+ omdev-0.0.0.dev165.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
168
+ omdev-0.0.0.dev165.dist-info/METADATA,sha256=djJeiQrUAVjts2OjpttnE69PQiKuV_wm3QIHBEWaS8A,1760
169
+ omdev-0.0.0.dev165.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
170
+ omdev-0.0.0.dev165.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
171
+ omdev-0.0.0.dev165.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
172
+ omdev-0.0.0.dev165.dist-info/RECORD,,