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
code_node/__init__.py ADDED
@@ -0,0 +1,40 @@
1
+ """
2
+ code-node: see how Python code works, read with ast and never run. A graph of who calls whom from an entry point and
3
+ what each path reaches (AWS calls, HTTP, environment variables), and the steps of each call.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from .analysis.graph import CodeGraph, CodeSpan, GraphEdge, GraphNode, code_graph
9
+ from .analysis.mapping import map_code
10
+ from .analysis.model import CodeMap, Deployment, LibraryCall
11
+ from .deploys import Deploy, find_deployment, register_deploy
12
+ from .libraries import Library, register_library
13
+ from .platforms import Platform, get_platform, register_platform
14
+ from .project import EntryPoint, Project, entry_points, read_project
15
+
16
+ __version__ = "0.1.0"
17
+
18
+ __all__ = [
19
+ "CodeGraph",
20
+ "CodeMap",
21
+ "CodeSpan",
22
+ "Deploy",
23
+ "Deployment",
24
+ "EntryPoint",
25
+ "GraphEdge",
26
+ "GraphNode",
27
+ "Library",
28
+ "LibraryCall",
29
+ "Platform",
30
+ "Project",
31
+ "code_graph",
32
+ "entry_points",
33
+ "find_deployment",
34
+ "get_platform",
35
+ "map_code",
36
+ "read_project",
37
+ "register_deploy",
38
+ "register_library",
39
+ "register_platform",
40
+ ]
code_node/__main__.py ADDED
@@ -0,0 +1,7 @@
1
+ """
2
+ python -m code_node: the code-node command.
3
+ """
4
+
5
+ from .cli import main
6
+
7
+ raise SystemExit(main())
@@ -0,0 +1,3 @@
1
+ """
2
+ Reading the code: ast in, a CodeMap and a CodeGraph out. Pure, and nothing in the code is run.
3
+ """
@@ -0,0 +1,197 @@
1
+ """
2
+ AWS operations named from botocore's own service models (no AWS call), the IAM
3
+ action each needs, and the event a handler takes.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import base64
9
+ import functools
10
+ import json
11
+ import re
12
+ from typing import TYPE_CHECKING, Any, Iterable
13
+
14
+ from .terms import _IAM_ACTIONS, _IAM_PREFIXES, _READS_PREFIXES, _RUNS_PREFIXES, _SENDS_PREFIXES, _SERVICE_NAMES
15
+
16
+ if TYPE_CHECKING:
17
+ from .model import EventField
18
+
19
+
20
+ def _service_name(service: str) -> str:
21
+ """
22
+ 's3' -> 'S3', 'dynamodb' -> 'DynamoDB', 'stepfunctions' -> 'Step Functions': the name people use.
23
+ """
24
+ if service in _SERVICE_NAMES:
25
+ return _SERVICE_NAMES[service]
26
+ model = _aws_model(service)
27
+ return model["name"] if model else service
28
+
29
+
30
+ @functools.lru_cache(maxsize=1)
31
+ def _botocore_loader() -> Any:
32
+ from botocore.loaders import create_loader
33
+
34
+ return create_loader()
35
+
36
+
37
+ def _tokens(value: Any) -> tuple[str, ...]:
38
+ """
39
+ A paginator's token setting as names: 'NextMarker || Contents[-1].Key' -> ('NextMarker',).
40
+ """
41
+ values = value if isinstance(value, list) else [value] if value else []
42
+ return tuple(m.group(0) for v in values if (m := re.match(r"\w+", str(v))))
43
+
44
+
45
+ @functools.lru_cache(maxsize=256)
46
+ def _aws_model(service: str) -> dict[str, Any] | None:
47
+ """
48
+ What botocore's own copy of a service's API says, read from the files boto3 installs (no AWS call): its
49
+ operations by method name, which of them come in pages, its name and its IAM prefix. None for a service this
50
+ boto3 doesn't know.
51
+ """
52
+ if not service or not re.fullmatch(r"[a-z0-9-]+", service):
53
+ return None
54
+ try:
55
+ from botocore import xform_name
56
+ from botocore.exceptions import BotoCoreError
57
+ except ImportError: # without botocore, AWS calls are known only by their methods' names
58
+ return None
59
+
60
+ loader = _botocore_loader()
61
+ try:
62
+ model = loader.load_service_model(service, "service-2")
63
+ except (BotoCoreError, OSError, ValueError):
64
+ return None
65
+ try:
66
+ pages = loader.load_service_model(service, "paginators-1").get("pagination", {})
67
+ except (BotoCoreError, OSError, ValueError):
68
+ pages = {}
69
+ meta = model.get("metadata", {})
70
+ return {
71
+ "ops": {xform_name(op): op for op in model.get("operations", {})},
72
+ "pages": {
73
+ op: (
74
+ frozenset(_tokens(spec.get("input_token"))),
75
+ _tokens(spec.get("output_token")) + _tokens(spec.get("more_results")),
76
+ str(spec.get("limit_key") or ""),
77
+ )
78
+ for op, spec in pages.items()
79
+ },
80
+ "name": meta.get("serviceId") or service,
81
+ "iam": meta.get("signingName") or meta.get("endpointPrefix") or service,
82
+ }
83
+
84
+
85
+ def _access(operation: str) -> str:
86
+ """
87
+ What an AWS operation does to what it touches: 'reads', 'writes', 'sends' (a message or an event) or 'runs'
88
+ (invokes or starts something).
89
+ """
90
+ if operation.startswith(_SENDS_PREFIXES):
91
+ return "sends"
92
+ if operation.startswith(_RUNS_PREFIXES):
93
+ return "runs"
94
+ return "reads" if operation.startswith(_READS_PREFIXES) else "writes"
95
+
96
+
97
+ def _iam_action(service: str, operation: str) -> str:
98
+ """
99
+ The IAM action an operation needs: ('s3', 'ListObjectsV2') -> 's3:ListBucket'.
100
+ """
101
+ if (service, operation) in _IAM_ACTIONS:
102
+ return _IAM_ACTIONS[(service, operation)]
103
+ model = _aws_model(service)
104
+ prefix = _IAM_PREFIXES.get(service) or (model["iam"] if model else service)
105
+ return f"{prefix}:{operation}"
106
+
107
+
108
+ def _event_label(path: tuple[str, ...]) -> str:
109
+ """
110
+ ('Records', '[]', 'body', '{json}', 'bucket') -> 'Records[].body (JSON).bucket'.
111
+ """
112
+ out = ""
113
+ for part in path:
114
+ if part == "[]":
115
+ out += "[]"
116
+ elif part in ("{json}", "{base64}"):
117
+ out += " (JSON)" if part == "{json}" else " (base64)"
118
+ else:
119
+ out += ("." if out else "") + part
120
+ return out
121
+
122
+
123
+ def _event_kind(fields: Iterable[EventField]) -> str | None:
124
+ """
125
+ What sends the event, from the fields the code reads: 'an SQS batch', 'an HTTP request', ... None when it
126
+ can't tell.
127
+ """
128
+ paths = [tuple(p.lower() for p in f.path) for f in fields]
129
+
130
+ def has(*parts: str) -> bool:
131
+ return any(path[: len(parts)] == parts for path in paths)
132
+
133
+ for parts, kind in (
134
+ (("records", "[]", "s3"), "an S3 event"),
135
+ (("records", "[]", "sns"), "an SNS message"),
136
+ (("records", "[]", "dynamodb"), "a DynamoDB stream batch"),
137
+ (("records", "[]", "kinesis"), "a Kinesis batch"),
138
+ (("records", "[]", "body"), "an SQS batch"),
139
+ (("records", "[]", "receipthandle"), "an SQS batch"),
140
+ (("records", "[]", "messageid"), "an SQS batch"),
141
+ (("awslogs",), "CloudWatch Logs"),
142
+ (("detail-type",), "an EventBridge event"),
143
+ (("actiongroup",), "a Bedrock agent action"),
144
+ (("apipath",), "a Bedrock agent action"),
145
+ ):
146
+ if has(*parts):
147
+ return kind
148
+ if any(
149
+ has(key)
150
+ for key in (
151
+ "httpmethod",
152
+ "pathparameters",
153
+ "querystringparameters",
154
+ "requestcontext",
155
+ "headers",
156
+ "rawpath",
157
+ "routekey",
158
+ "multivalueheaders",
159
+ "rawquerystring",
160
+ )
161
+ ):
162
+ return "an HTTP request"
163
+ if has("detail") and has("source"):
164
+ return "an EventBridge event"
165
+ if has("records"):
166
+ return "a batch of records"
167
+ return None
168
+
169
+
170
+ def _sample_event(fields: Iterable[EventField]) -> Any:
171
+ """
172
+ A test event with every field the code reads, its values '...': {'Records': [{'body': '{"bucket": "..."}'}]},
173
+ for the console's Test tab. JSON read from a text field is written as text, the way it arrives.
174
+ """
175
+ root: dict[str, Any] = {}
176
+ for f in fields:
177
+ if "*" in f.path:
178
+ continue
179
+ node = root
180
+ for part in f.path:
181
+ node = node.setdefault(part, {})
182
+
183
+ def finish(node: dict[str, Any]) -> Any:
184
+ if not node:
185
+ return "..."
186
+ if "{json}" in node:
187
+ return json.dumps(finish(node["{json}"]))
188
+ if "{base64}" in node:
189
+ inner = finish(node["{base64}"])
190
+ text = inner if isinstance(inner, str) else json.dumps(inner)
191
+ return base64.b64encode(text.encode()).decode() if text != "..." else "..."
192
+ keys = [k for k in node if k != "[]"]
193
+ if not keys and "[]" in node:
194
+ return [finish(node["[]"])]
195
+ return {k: finish(node[k]) for k in keys}
196
+
197
+ return finish(root)