FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
HydroModPy/tools/audit_profile_coverage.py at dev · HydroModPy/HydroModPy · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
HydroModPy
/
HydroModPy
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
11
Code
Issues
18
Pull requests
1
Discussions
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Discussions
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
HydroModPy
/
tools
/
audit_profile_coverage.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
122 lines (103 loc) · 3.69 KB
Breadcrumbs
HydroModPy
/
tools
/
audit_profile_coverage.py
Copy path
File metadata and controls
122 lines (103 loc) · 3.69 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""Verify every Annotated Pydantic field in hydromodpy has a Profile tag.
Exits 1 with the list of uncovered fields on stderr. Used in CI to prevent
drift of the visibility classification introduced in v0.6.
The audit walks every :class:`HydroModelBase` subclass reachable from the
registered top-level configs and inspects their ``model_fields``. A field
is *covered* when its ``Annotated[...]`` metadata carries a
:class:`Profile` enum tag.
When a field carries a forward-reference annotation, Pydantic's runtime
``metadata`` list is empty even if the source does tag the field. In that
case the audit falls back to an AST inspection of the class source to avoid
false positives.
"""
from
__future__
import
annotations
import
ast
import
inspect
import
sys
from
pathlib
import
Path
from
hydromodpy
.
core
.
config_kit
.
base
import
HydroModelBase
from
hydromodpy
.
core
.
config_kit
.
profile
import
Profile
def
_runtime_has_profile
(
field_info
)
->
bool
:
metadata
=
getattr
(
field_info
,
"metadata"
, ())
return
any
(
isinstance
(
m
,
Profile
)
for
m
in
metadata
)
def
_ast_has_profile
(
src_file
:
Path
,
qualname
:
str
,
field_name
:
str
)
->
bool
:
"""Return True if the source class body tags *field_name* with a Profile enum.
Used as a fallback when runtime metadata is empty because the annotation
is a :class:`typing.ForwardRef` string.
"""
try
:
text
=
src_file
.
read_text
(
encoding
=
"utf-8"
)
except
OSError
:
return
False
try
:
tree
=
ast
.
parse
(
text
)
except
SyntaxError
:
return
False
parts
=
qualname
.
split
(
"."
)
parent
:
ast
.
AST
=
tree
for
part
in
parts
:
found
=
None
for
child
in
ast
.
iter_child_nodes
(
parent
):
if
isinstance
(
child
,
ast
.
ClassDef
)
and
child
.
name
==
part
:
found
=
child
break
if
found
is
None
:
return
False
parent
=
found
assert
isinstance
(
parent
,
ast
.
ClassDef
)
for
stmt
in
parent
.
body
:
if
not
(
isinstance
(
stmt
,
ast
.
AnnAssign
)
and
isinstance
(
stmt
.
target
,
ast
.
Name
)
and
stmt
.
target
.
id
==
field_name
):
continue
ann
=
stmt
.
annotation
if
not
(
isinstance
(
ann
,
ast
.
Subscript
)
and
isinstance
(
ann
.
value
,
ast
.
Name
)
and
ann
.
value
.
id
==
"Annotated"
):
return
False
slice_node
=
ann
.
slice
if
not
isinstance
(
slice_node
,
ast
.
Tuple
):
return
False
for
tag
in
slice_node
.
elts
[
1
:]:
if
(
isinstance
(
tag
,
ast
.
Attribute
)
and
isinstance
(
tag
.
value
,
ast
.
Name
)
and
tag
.
value
.
id
==
"Profile"
):
return
True
return
False
return
False
def
walk
(
cls
=
HydroModelBase
,
seen
=
None
):
seen
=
seen
if
seen
is
not
None
else
set
()
for
sub
in
cls
.
__subclasses__
():
if
sub
in
seen
:
continue
seen
.
add
(
sub
)
for
field_name
,
info
in
sub
.
model_fields
.
items
():
if
_runtime_has_profile
(
info
):
continue
try
:
src_file
=
inspect
.
getsourcefile
(
sub
)
except
TypeError
:
src_file
=
None
if
src_file
is
not
None
and
_ast_has_profile
(
Path
(
src_file
),
sub
.
__qualname__
,
field_name
):
continue
yield
f"
{
sub
.
__module__
}
.
{
sub
.
__qualname__
}
.
{
field_name
}
"
yield
from
walk
(
sub
,
seen
)
def
main
()
->
int
:
missing
=
sorted
(
set
(
walk
()))
for
entry
in
missing
:
print
(
entry
,
file
=
sys
.
stderr
)
print
(
f"
\n
{
len
(
missing
)
}
field(s) missing Profile metadata"
,
file
=
sys
.
stderr
,
)
return
1
if
missing
else
0
if
__name__
==
"__main__"
:
sys
.
exit
(
main
())
Back
|
FazBrowse Home
|
New Git URL