code-node-graph 0.1.0__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.
Files changed (178) hide show
  1. code_node/__init__.py +40 -0
  2. code_node/__main__.py +7 -0
  3. code_node/analysis/__init__.py +3 -0
  4. code_node/analysis/aws_model.py +197 -0
  5. code_node/analysis/findings.py +462 -0
  6. code_node/analysis/graph.py +780 -0
  7. code_node/analysis/mapping.py +114 -0
  8. code_node/analysis/model.py +289 -0
  9. code_node/analysis/reader.py +1926 -0
  10. code_node/analysis/syntax.py +220 -0
  11. code_node/analysis/terms.py +505 -0
  12. code_node/cli.py +316 -0
  13. code_node/deploys/__init__.py +321 -0
  14. code_node/deploys/sam.py +371 -0
  15. code_node/deploys/serverless.py +117 -0
  16. code_node/kit/__init__.py +4 -0
  17. code_node/kit/blocks.py +184 -0
  18. code_node/kit/fmt.py +389 -0
  19. code_node/kit/highlight.py +131 -0
  20. code_node/kit/render.py +1066 -0
  21. code_node/kit/text.py +81 -0
  22. code_node/libraries/__init__.py +333 -0
  23. code_node/libraries/llm.py +124 -0
  24. code_node/libraries/mongo.py +169 -0
  25. code_node/libraries/redis.py +253 -0
  26. code_node/libraries/sql.py +232 -0
  27. code_node/pages.py +92 -0
  28. code_node/platforms/__init__.py +203 -0
  29. code_node/platforms/aws_lambda.py +290 -0
  30. code_node/platforms/web.py +301 -0
  31. code_node/project.py +402 -0
  32. code_node/report/__init__.py +3 -0
  33. code_node/report/brands.py +244 -0
  34. code_node/report/build.py +119 -0
  35. code_node/report/editor.py +40 -0
  36. code_node/report/graph.py +1411 -0
  37. code_node/report/logos/README.md +8 -0
  38. code_node/report/logos/anthropic-icon.svg +1 -0
  39. code_node/report/logos/aws-amplify.svg +1 -0
  40. code_node/report/logos/aws-api-gateway.svg +1 -0
  41. code_node/report/logos/aws-appflow.svg +1 -0
  42. code_node/report/logos/aws-appsync.svg +1 -0
  43. code_node/report/logos/aws-athena.svg +1 -0
  44. code_node/report/logos/aws-backup.svg +1 -0
  45. code_node/report/logos/aws-batch.svg +1 -0
  46. code_node/report/logos/aws-certificate-manager.svg +1 -0
  47. code_node/report/logos/aws-cloudformation.svg +1 -0
  48. code_node/report/logos/aws-cloudfront.svg +1 -0
  49. code_node/report/logos/aws-cloudtrail.svg +1 -0
  50. code_node/report/logos/aws-cloudwatch.svg +1 -0
  51. code_node/report/logos/aws-codebuild.svg +1 -0
  52. code_node/report/logos/aws-codecommit.svg +1 -0
  53. code_node/report/logos/aws-codedeploy.svg +1 -0
  54. code_node/report/logos/aws-codepipeline.svg +1 -0
  55. code_node/report/logos/aws-cognito.svg +1 -0
  56. code_node/report/logos/aws-config.svg +1 -0
  57. code_node/report/logos/aws-documentdb.svg +1 -0
  58. code_node/report/logos/aws-dynamodb.svg +1 -0
  59. code_node/report/logos/aws-ec2.svg +1 -0
  60. code_node/report/logos/aws-ecs.svg +1 -0
  61. code_node/report/logos/aws-eks.svg +1 -0
  62. code_node/report/logos/aws-elasticache.svg +1 -0
  63. code_node/report/logos/aws-elb.svg +1 -0
  64. code_node/report/logos/aws-eventbridge.svg +1 -0
  65. code_node/report/logos/aws-glacier.svg +1 -0
  66. code_node/report/logos/aws-glue.svg +1 -0
  67. code_node/report/logos/aws-iam.svg +1 -0
  68. code_node/report/logos/aws-keyspaces.svg +1 -0
  69. code_node/report/logos/aws-kinesis.svg +1 -0
  70. code_node/report/logos/aws-kms.svg +1 -0
  71. code_node/report/logos/aws-lake-formation.svg +1 -0
  72. code_node/report/logos/aws-lambda.svg +1 -0
  73. code_node/report/logos/aws-lightsail.svg +1 -0
  74. code_node/report/logos/aws-mq.svg +1 -0
  75. code_node/report/logos/aws-msk.svg +1 -0
  76. code_node/report/logos/aws-neptune.svg +1 -0
  77. code_node/report/logos/aws-open-search.svg +1 -0
  78. code_node/report/logos/aws-quicksight.svg +1 -0
  79. code_node/report/logos/aws-rds.svg +1 -0
  80. code_node/report/logos/aws-redshift.svg +1 -0
  81. code_node/report/logos/aws-route53.svg +1 -0
  82. code_node/report/logos/aws-s3.svg +1 -0
  83. code_node/report/logos/aws-secrets-manager.svg +1 -0
  84. code_node/report/logos/aws-ses.svg +1 -0
  85. code_node/report/logos/aws-shield.svg +1 -0
  86. code_node/report/logos/aws-sns.svg +1 -0
  87. code_node/report/logos/aws-sqs.svg +1 -0
  88. code_node/report/logos/aws-step-functions.svg +1 -0
  89. code_node/report/logos/aws-systems-manager.svg +1 -0
  90. code_node/report/logos/aws-timestream.svg +1 -0
  91. code_node/report/logos/aws-waf.svg +1 -0
  92. code_node/report/logos/aws-xray.svg +1 -0
  93. code_node/report/logos/aws.svg +1 -0
  94. code_node/report/logos/azure-icon.svg +1 -0
  95. code_node/report/logos/django-icon.svg +1 -0
  96. code_node/report/logos/elasticsearch.svg +1 -0
  97. code_node/report/logos/fastapi-icon.svg +1 -0
  98. code_node/report/logos/firebase-icon.svg +1 -0
  99. code_node/report/logos/flask.svg +1 -0
  100. code_node/report/logos/google-cloud.svg +1 -0
  101. code_node/report/logos/kafka-icon.svg +1 -0
  102. code_node/report/logos/mariadb-icon.svg +1 -0
  103. code_node/report/logos/mongodb-icon.svg +1 -0
  104. code_node/report/logos/mysql-icon.svg +1 -0
  105. code_node/report/logos/openai-icon.svg +1 -0
  106. code_node/report/logos/postgresql.svg +1 -0
  107. code_node/report/logos/python.svg +1 -0
  108. code_node/report/logos/rabbitmq-icon.svg +1 -0
  109. code_node/report/logos/redis.svg +1 -0
  110. code_node/report/logos/serverless.svg +1 -0
  111. code_node/report/logos/slack-icon.svg +1 -0
  112. code_node/report/logos/small/aws-api-gateway.svg +1 -0
  113. code_node/report/logos/small/aws-appflow.svg +1 -0
  114. code_node/report/logos/small/aws-appsync.svg +1 -0
  115. code_node/report/logos/small/aws-athena.svg +1 -0
  116. code_node/report/logos/small/aws-backup.svg +1 -0
  117. code_node/report/logos/small/aws-batch.svg +1 -0
  118. code_node/report/logos/small/aws-certificate-manager.svg +1 -0
  119. code_node/report/logos/small/aws-cloudformation.svg +1 -0
  120. code_node/report/logos/small/aws-cloudfront.svg +1 -0
  121. code_node/report/logos/small/aws-cloudtrail.svg +1 -0
  122. code_node/report/logos/small/aws-cloudwatch.svg +1 -0
  123. code_node/report/logos/small/aws-codebuild.svg +1 -0
  124. code_node/report/logos/small/aws-codecommit.svg +1 -0
  125. code_node/report/logos/small/aws-codedeploy.svg +1 -0
  126. code_node/report/logos/small/aws-codepipeline.svg +1 -0
  127. code_node/report/logos/small/aws-cognito.svg +1 -0
  128. code_node/report/logos/small/aws-config.svg +1 -0
  129. code_node/report/logos/small/aws-documentdb.svg +1 -0
  130. code_node/report/logos/small/aws-dynamodb.svg +1 -0
  131. code_node/report/logos/small/aws-ec2.svg +1 -0
  132. code_node/report/logos/small/aws-ecs.svg +1 -0
  133. code_node/report/logos/small/aws-eks.svg +1 -0
  134. code_node/report/logos/small/aws-elasticache.svg +1 -0
  135. code_node/report/logos/small/aws-elb.svg +1 -0
  136. code_node/report/logos/small/aws-eventbridge.svg +1 -0
  137. code_node/report/logos/small/aws-glacier.svg +1 -0
  138. code_node/report/logos/small/aws-glue.svg +1 -0
  139. code_node/report/logos/small/aws-iam.svg +1 -0
  140. code_node/report/logos/small/aws-keyspaces.svg +1 -0
  141. code_node/report/logos/small/aws-kinesis.svg +1 -0
  142. code_node/report/logos/small/aws-kms.svg +1 -0
  143. code_node/report/logos/small/aws-lake-formation.svg +1 -0
  144. code_node/report/logos/small/aws-lambda.svg +1 -0
  145. code_node/report/logos/small/aws-mq.svg +1 -0
  146. code_node/report/logos/small/aws-msk.svg +1 -0
  147. code_node/report/logos/small/aws-neptune.svg +1 -0
  148. code_node/report/logos/small/aws-open-search.svg +1 -0
  149. code_node/report/logos/small/aws-quicksight.svg +1 -0
  150. code_node/report/logos/small/aws-rds.svg +1 -0
  151. code_node/report/logos/small/aws-redshift.svg +1 -0
  152. code_node/report/logos/small/aws-route53.svg +1 -0
  153. code_node/report/logos/small/aws-s3.svg +1 -0
  154. code_node/report/logos/small/aws-secrets-manager.svg +1 -0
  155. code_node/report/logos/small/aws-ses.svg +1 -0
  156. code_node/report/logos/small/aws-shield.svg +1 -0
  157. code_node/report/logos/small/aws-sns.svg +1 -0
  158. code_node/report/logos/small/aws-sqs.svg +1 -0
  159. code_node/report/logos/small/aws-step-functions.svg +1 -0
  160. code_node/report/logos/small/aws-systems-manager.svg +1 -0
  161. code_node/report/logos/small/aws-timestream.svg +1 -0
  162. code_node/report/logos/small/aws-waf.svg +1 -0
  163. code_node/report/logos/small/aws-xray.svg +1 -0
  164. code_node/report/logos/snowflake-icon.svg +1 -0
  165. code_node/report/logos/sqlite.svg +1 -0
  166. code_node/report/logos/stripe.svg +1 -0
  167. code_node/report/logos/supabase-icon.svg +1 -0
  168. code_node/report/logos/twilio-icon.svg +1 -0
  169. code_node/report/outline.py +219 -0
  170. code_node/report/style.py +358 -0
  171. code_node/report/tabs.py +73 -0
  172. code_node/report/walk.py +624 -0
  173. code_node/serve.py +277 -0
  174. code_node_graph-0.1.0.dist-info/METADATA +165 -0
  175. code_node_graph-0.1.0.dist-info/RECORD +178 -0
  176. code_node_graph-0.1.0.dist-info/WHEEL +4 -0
  177. code_node_graph-0.1.0.dist-info/entry_points.txt +2 -0
  178. code_node_graph-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,220 @@
1
+ """
2
+ Small readings of Python syntax the code reader shares: names, constants, docstrings,
3
+ the lines of a definition, and secrets written into the code.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import ast
9
+ import copy
10
+ import re
11
+ import warnings
12
+ from typing import Any, Iterable
13
+
14
+ from ..kit.text import _clip
15
+
16
+ from .terms import _AWS_KEY_RE, _NOT_SECRET_NAME_RE, _PLACEHOLDERS, _SECRET_NAME_RE
17
+
18
+
19
+ def _parse(text: str, filename: str = "<unknown>", mode: str = "exec") -> Any:
20
+ """
21
+ ast.parse, with the warnings Python gives while parsing (a SyntaxWarning for '\\d' in a plain string) kept quiet:
22
+ they're about the code read, and would only clutter the terminal.
23
+ """
24
+ with warnings.catch_warnings():
25
+ warnings.simplefilter("ignore")
26
+ return ast.parse(text, filename=filename, mode=mode)
27
+
28
+
29
+ _OPPOSITES = {
30
+ ast.Is: ast.IsNot,
31
+ ast.IsNot: ast.Is,
32
+ ast.In: ast.NotIn,
33
+ ast.NotIn: ast.In,
34
+ ast.Eq: ast.NotEq,
35
+ ast.NotEq: ast.Eq,
36
+ } # a comparison's operator -> the one that's true when it isn't
37
+
38
+
39
+ def _negated(test: Any) -> str:
40
+ """
41
+ The opposite of an if's test, as code: 'check(order)' for `not check(order)`, 'x is not None' for `x is None`,
42
+ 'not (a or b)' for `a or b`.
43
+ """
44
+ if isinstance(test, ast.UnaryOp) and isinstance(test.op, ast.Not):
45
+ return _unparse(test.operand, 110)
46
+ if isinstance(test, ast.Compare) and len(test.ops) == 1 and type(test.ops[0]) in _OPPOSITES:
47
+ flipped = ast.Compare(test.left, [_OPPOSITES[type(test.ops[0])]()], test.comparators)
48
+ return _unparse(flipped, 110)
49
+ code = _unparse(test, 110)
50
+ return f"not ({code})" if isinstance(test, (ast.BoolOp, ast.IfExp, ast.NamedExpr, ast.Lambda)) else f"not {code}"
51
+
52
+
53
+ def _ends_early(body: list[Any]) -> bool:
54
+ """
55
+ Whether a block always leaves where it is: its last line returns, raises, continues or breaks.
56
+ """
57
+ return bool(body) and isinstance(body[-1], (ast.Return, ast.Raise, ast.Continue, ast.Break))
58
+
59
+
60
+ def _secret_spots(node: Any) -> list[tuple[str, Any]]:
61
+ """
62
+ The secret-looking literals in a piece of code, with the name each is given to: a password=, token= or key given
63
+ as text (in an assignment, a call or a dict), and any AWS access key.
64
+ """
65
+ found: list[tuple[str, Any]] = []
66
+ for n in ast.walk(node):
67
+ pairs: list[tuple[Any, Any]] = []
68
+ if isinstance(n, (ast.Assign, ast.AnnAssign)) and n.value is not None:
69
+ for target in n.targets if isinstance(n, ast.Assign) else [n.target]:
70
+ pairs.append(
71
+ (
72
+ target.id
73
+ if isinstance(target, ast.Name)
74
+ else target.attr
75
+ if isinstance(target, ast.Attribute)
76
+ else _const(target.slice)
77
+ if isinstance(target, ast.Subscript)
78
+ else None,
79
+ n.value,
80
+ )
81
+ )
82
+ elif isinstance(n, ast.Call):
83
+ pairs += [(kw.arg, kw.value) for kw in n.keywords if kw.arg]
84
+ elif isinstance(n, ast.Dict):
85
+ pairs += [(_const(k), v) for k, v in zip(n.keys, n.values) if k is not None]
86
+ for name, value in pairs:
87
+ text = _const(value)
88
+ if (
89
+ isinstance(name, str)
90
+ and isinstance(text, str)
91
+ and _SECRET_NAME_RE.search(name)
92
+ and not _NOT_SECRET_NAME_RE.search(name)
93
+ and _secret_value(text)
94
+ ):
95
+ found.append((name, value))
96
+ if isinstance(n, ast.Constant) and isinstance(n.value, str) and _AWS_KEY_RE.search(n.value):
97
+ found.append(("an AWS access key", n))
98
+ return found
99
+
100
+
101
+ class _Hide(ast.NodeTransformer):
102
+ """
103
+ Puts '…' in place of the literals at these places (line, column).
104
+ """
105
+
106
+ def __init__(self, places: set[tuple[int, int]]):
107
+ self.places = places
108
+
109
+ def visit_Constant(self, node: ast.Constant) -> Any:
110
+ if (getattr(node, "lineno", 0), getattr(node, "col_offset", 0)) in self.places:
111
+ return ast.copy_location(ast.Constant("…"), node)
112
+ return node
113
+
114
+
115
+ def _unparse(node: Any, width: int = 140) -> str:
116
+ """
117
+ A piece of code on one line, as Python writes it back from the tree: "s3.get_object(Bucket=b, Key=k)". A
118
+ secret-looking literal in it shows as '…'.
119
+ """
120
+ try:
121
+ if isinstance(node, ast.AST) and not isinstance(node, (ast.arguments, ast.pattern)):
122
+ places = {(getattr(v, "lineno", 0), getattr(v, "col_offset", 0)) for _, v in _secret_spots(node)}
123
+ if places:
124
+ node = _Hide(places).visit(copy.deepcopy(node))
125
+ text = ast.unparse(node)
126
+ except (AttributeError, ValueError, TypeError, RecursionError):
127
+ return "…"
128
+ return _clip(" ".join(text.split()), width)
129
+
130
+
131
+ def _target(node: Any) -> str:
132
+ """
133
+ A loop's target as written: 'i, record', not '(i, record)'.
134
+ """
135
+ if isinstance(node, ast.Tuple) and node.elts:
136
+ return ", ".join(_unparse(elt, 30) for elt in node.elts)
137
+ return _unparse(node, 40)
138
+
139
+
140
+ def _a(name: str) -> str:
141
+ """
142
+ 'a' or 'an' before a service's name, as it's said: an S3, an SQS, a DynamoDB, an EventBridge, a KMS.
143
+ """
144
+ if re.match(r"[A-Z][A-Z0-9]", name): # said letter by letter
145
+ return "an" if name[0] in "AEFHILMNORSX" else "a"
146
+ return "an" if name[:1].lower() in "aeiou" else "a"
147
+
148
+
149
+ def _const(node: Any) -> Any:
150
+ return node.value if isinstance(node, ast.Constant) else None
151
+
152
+
153
+ def _names_in(node: Any, names: Iterable[str]) -> bool:
154
+ """
155
+ Whether an `except` catches one of these exceptions: except ImportError, except (KeyError, Exception).
156
+ """
157
+ if node is None:
158
+ return False
159
+ if isinstance(node, ast.Tuple):
160
+ return any(_names_in(elt, names) for elt in node.elts)
161
+ return (node.id if isinstance(node, ast.Name) else getattr(node, "attr", "")) in set(names)
162
+
163
+
164
+ def _swallows(handler: ast.ExceptHandler) -> bool:
165
+ """
166
+ An `except` that catches every error and does nothing with it: no log line, no raise, nothing returned.
167
+ """
168
+ if not (handler.type is None or _names_in(handler.type, ("Exception", "BaseException"))):
169
+ return False
170
+ for stmt in handler.body:
171
+ if isinstance(stmt, (ast.Pass, ast.Continue, ast.Break)):
172
+ continue
173
+ if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Constant):
174
+ continue
175
+ if isinstance(stmt, ast.Return) and (stmt.value is None or isinstance(stmt.value, ast.Constant)):
176
+ continue
177
+ return False
178
+ return True
179
+
180
+
181
+ def _is_main_guard(test: Any) -> bool:
182
+ """
183
+ `if __name__ == "__main__":`, which never runs when the code is imported.
184
+ """
185
+ return (
186
+ isinstance(test, ast.Compare)
187
+ and isinstance(test.left, ast.Name)
188
+ and test.left.id == "__name__"
189
+ and any(_const(c) == "__main__" for c in test.comparators)
190
+ )
191
+
192
+
193
+ def _doc_line(node: Any) -> str:
194
+ doc = ast.get_docstring(node) or ""
195
+ return _clip(doc.strip().split("\n\n")[0].replace("\n", " ").strip(), 160)
196
+
197
+
198
+ def _def_line(node: Any) -> str:
199
+ """
200
+ 'def connect(host, password):'.
201
+ """
202
+ prefix = "async def" if isinstance(node, ast.AsyncFunctionDef) else "def"
203
+ return f"{prefix} {node.name}({_unparse(node.args, 100)}):"
204
+
205
+
206
+ def _secret_value(value: str) -> bool:
207
+ """
208
+ Whether a literal looks like a real secret rather than a name, a path or a placeholder.
209
+ """
210
+ text = value.strip()
211
+ return (
212
+ len(text) >= 6
213
+ and not re.search(r"\s", text)
214
+ and not re.fullmatch(r"[A-Z0-9_]+", text)
215
+ and not text.startswith(("arn:", "/", "{", "$", "<", "%", "http", "s3://", "projects/"))
216
+ and text.lower() not in _PLACEHOLDERS
217
+ and "xxx" not in text.lower()
218
+ and len(set(text)) > 2
219
+ and not text.lower().startswith("your")
220
+ )