codeanalyzer-python 0.3.0__py3-none-any.whl → 0.3.2__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.
@@ -0,0 +1,61 @@
1
+ """Provenance capture: repository (git) and analyzer identity for a snapshot.
2
+
3
+ Runs git only as a subprocess query against the analyzed project directory —
4
+ never mutates anything. Absence of git (no repo, no binary) degrades to None.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import subprocess
9
+ from importlib.metadata import PackageNotFoundError, version as _pkg_version
10
+ from pathlib import Path
11
+ from typing import Optional, Union
12
+ from urllib.parse import urlsplit, urlunsplit
13
+
14
+ from codeanalyzer.schema.py_schema import PyAnalyzerInfo, PyRepositoryInfo
15
+
16
+
17
+ def _strip_userinfo(uri: str) -> str:
18
+ """Drop credentials from URL-style remotes; scp-style git@host:path is a
19
+ username, not a secret, and parses with no netloc — left intact."""
20
+ parts = urlsplit(uri)
21
+ if parts.netloc and "@" in parts.netloc:
22
+ return urlunsplit(parts._replace(netloc=parts.netloc.rpartition("@")[2]))
23
+ return uri
24
+
25
+
26
+ def _git(project_dir: Union[Path, str], *args: str) -> Optional[str]:
27
+ """One git query; None on any failure (no repo, no git, timeout)."""
28
+ try:
29
+ result = subprocess.run(
30
+ ["git", "-C", str(project_dir), *args],
31
+ capture_output=True, text=True, timeout=10, check=False,
32
+ )
33
+ except (OSError, subprocess.TimeoutExpired):
34
+ return None
35
+ if result.returncode != 0:
36
+ return None
37
+ return result.stdout.strip()
38
+
39
+
40
+ def repository_info(project_dir: Union[Path, str]) -> Optional[PyRepositoryInfo]:
41
+ """Git provenance of ``project_dir``, or None when it isn't a git checkout."""
42
+ revision = _git(project_dir, "rev-parse", "HEAD")
43
+ if not revision:
44
+ return None
45
+ raw_uri = _git(project_dir, "remote", "get-url", "origin")
46
+ uri = _strip_userinfo(raw_uri) if raw_uri else None
47
+ status = _git(project_dir, "status", "--porcelain", "--untracked-files=no")
48
+ return PyRepositoryInfo(uri=uri, revision=revision, dirty=bool(status))
49
+
50
+
51
+ def analyzer_info(analysis_level: int) -> PyAnalyzerInfo:
52
+ """Identity + configuration of this analyzer run."""
53
+ try:
54
+ version = _pkg_version("codeanalyzer-python")
55
+ except PackageNotFoundError:
56
+ version = "unknown"
57
+ return PyAnalyzerInfo(
58
+ name="codeanalyzer-python",
59
+ version=version,
60
+ config={"analysis_level": analysis_level},
61
+ )
@@ -176,6 +176,7 @@ class PyImport(BaseModel):
176
176
  module: str
177
177
  name: str
178
178
  alias: Optional[str] = None
179
+ resolved_module: Optional[str] = None
179
180
  start_line: int = -1
180
181
  end_line: int = -1
181
182
  start_column: int = -1
@@ -240,6 +241,19 @@ class PyCallableParameter(BaseModel):
240
241
  end_column: int = -1
241
242
 
242
243
 
244
+ @builder
245
+ @msgpk
246
+ class PyCallArgument(BaseModel):
247
+ """One call-site argument: AST category + inferred type, kept separate.
248
+
249
+ The legacy ``PyCallsite.argument_types`` mixed these two vocabularies
250
+ in one list; this model is the disambiguated replacement (#86).
251
+ """
252
+
253
+ ast_kind: str
254
+ inferred_type: Optional[str] = None
255
+
256
+
243
257
  @builder
244
258
  @msgpk
245
259
  class PyCallsite(BaseModel):
@@ -249,6 +263,7 @@ class PyCallsite(BaseModel):
249
263
  receiver_expr: Optional[str] = None
250
264
  receiver_type: Optional[str] = None
251
265
  argument_types: List[str] = []
266
+ arguments: List[PyCallArgument] = []
252
267
  return_type: Optional[str] = None
253
268
  callee_signature: Optional[str] = None
254
269
  is_constructor_call: bool = False
@@ -295,6 +310,7 @@ class PyClassAttribute(BaseModel):
295
310
 
296
311
  name: str
297
312
  type: Optional[str] = None
313
+ initializer: Optional[str] = None
298
314
  comments: List[PyComment] = []
299
315
  start_line: int = -1
300
316
  end_line: int = -1
@@ -369,6 +385,26 @@ class PyExternalSymbol(BaseModel):
369
385
  module: Optional[str] = None # best-effort owning module, e.g. "requests"
370
386
 
371
387
 
388
+ @builder
389
+ @msgpk
390
+ class PyRepositoryInfo(BaseModel):
391
+ """Where the analyzed source came from: git provenance captured at analysis time."""
392
+
393
+ uri: Optional[str] = None
394
+ revision: str
395
+ dirty: bool = False
396
+
397
+
398
+ @builder
399
+ @msgpk
400
+ class PyAnalyzerInfo(BaseModel):
401
+ """Which analyzer produced this snapshot, and how it was configured."""
402
+
403
+ name: str
404
+ version: str
405
+ config: Dict[str, Any] = {}
406
+
407
+
372
408
  @builder
373
409
  @msgpk
374
410
  class PyApplication(BaseModel):
@@ -380,3 +416,5 @@ class PyApplication(BaseModel):
380
416
  # builtin members), keyed by signature. Populated by the analyzer so every
381
417
  # backend (JSON and Neo4j) shares one authoritative external-symbol set.
382
418
  external_symbols: Dict[str, PyExternalSymbol] = {}
419
+ analyzer: Optional[PyAnalyzerInfo] = None
420
+ repository: Optional[PyRepositoryInfo] = None
@@ -0,0 +1,67 @@
1
+ """Static resolution of import spellings against the analyzed module set.
2
+
3
+ Pure post-pass over a built ``PyApplication``: no filesystem access, no
4
+ sys.path semantics — a spelling resolves iff it names a module that was
5
+ itself analyzed (issue #82). External/library imports stay unresolved by
6
+ design and keep their :PyPackage projection.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from pathlib import Path
12
+ from typing import Dict, Optional, Union
13
+
14
+ from codeanalyzer.schema.py_schema import PyApplication
15
+
16
+
17
+ def _dotted_candidates(app: PyApplication, project_dir: Union[Path, str]) -> Dict[str, str]:
18
+ """dotted module path -> file_key, for every analyzed module.
19
+
20
+ ``pkg/util.py`` -> ``pkg.util``; ``pkg/__init__.py`` -> ``pkg``.
21
+ file_keys share project_dir's form (both come from the same CLI arg),
22
+ so os.path.relpath keeps mixed absolute/relative setups consistent.
23
+ """
24
+ mapping: Dict[str, str] = {}
25
+ for file_key in app.symbol_table:
26
+ rel = os.path.relpath(file_key, str(project_dir))
27
+ if rel.startswith(".."):
28
+ continue
29
+ parts = Path(rel).with_suffix("").parts
30
+ if parts and parts[-1] == "__init__":
31
+ parts = parts[:-1]
32
+ if parts:
33
+ mapping[".".join(parts)] = file_key
34
+ return mapping
35
+
36
+
37
+ def _resolve_one(
38
+ spelling: str, original_name: str, importer_rel_parts: tuple, candidates: Dict[str, str]
39
+ ) -> Optional[str]:
40
+ if spelling.startswith("."):
41
+ level = len(spelling) - len(spelling.lstrip("."))
42
+ suffix = spelling.lstrip(".")
43
+ # level 1 = the importer's own package; each extra dot walks one up.
44
+ package_parts = importer_rel_parts[:-1] # drop the filename
45
+ if level - 1 > len(package_parts):
46
+ return None
47
+ base = package_parts[: len(package_parts) - (level - 1)]
48
+ stems = list(base) + (suffix.split(".") if suffix else [])
49
+ else:
50
+ stems = spelling.split(".")
51
+ dotted = ".".join(stems)
52
+ with_name = f"{dotted}.{original_name}" if dotted else original_name
53
+ return candidates.get(with_name) or candidates.get(dotted)
54
+
55
+
56
+ def resolve_imports(app: PyApplication, project_dir: Union[Path, str]) -> None:
57
+ """Stamp ``resolved_module`` on every import of every module, in place."""
58
+ candidates = _dotted_candidates(app, project_dir)
59
+ for file_key, module in app.symbol_table.items():
60
+ rel = os.path.relpath(file_key, str(project_dir))
61
+ importer_parts = Path(rel).parts
62
+ for im in module.imports or []:
63
+ if not im.module:
64
+ im.resolved_module = None
65
+ continue
66
+ original = im.alias or im.name
67
+ im.resolved_module = _resolve_one(im.module, original, importer_parts, candidates)
@@ -1,5 +1,6 @@
1
1
  import ast
2
2
  import hashlib
3
+ import os
3
4
  import tokenize
4
5
  from ast import AST, ClassDef
5
6
  from io import StringIO
@@ -13,6 +14,7 @@ from jedi.api.project import Project
13
14
  from codeanalyzer.schema.py_schema import (
14
15
  PyCallable,
15
16
  PyCallableParameter,
17
+ PyCallArgument,
16
18
  PyCallsite,
17
19
  PyClass,
18
20
  PyClassAttribute,
@@ -28,7 +30,11 @@ class SymbolTableBuilder:
28
30
  """A class for building a symbol table for a Python project."""
29
31
 
30
32
  def __init__(self, project_dir: Union[Path, str], virtualenv: Union[Path, str, None]) -> None:
31
- self.project_dir = Path(project_dir)
33
+ # Jedi reports absolute Script paths, so a relative project_dir would
34
+ # crash every relative_to() fallback below. abspath (not resolve())
35
+ # keeps symlinks intact, matching Jedi's own absolute()-style
36
+ # normalization under symlinked roots like macOS /tmp.
37
+ self.project_dir = Path(os.path.abspath(project_dir))
32
38
  if virtualenv is None:
33
39
  # If no virtual environment is provided, create a jedi project without an environment.
34
40
  self.jedi_project: Project = jedi.Project(path=self.project_dir)
@@ -39,6 +45,16 @@ class SymbolTableBuilder:
39
45
  environment_path=Path(virtualenv) / "bin" / "python",
40
46
  )
41
47
 
48
+ def _fallback_signature(self, script_path: Union[Path, str], name: str) -> str:
49
+ """Path-derived qualified name used when Jedi can't name a definition.
50
+
51
+ Strips only the terminal ``.py`` suffix — a bare ``str.replace``
52
+ would also eat interior ``.py`` substrings and corrupt the module
53
+ prefix (``odoo/tools/pycompat.py`` → ``odoo.toolscompat``).
54
+ """
55
+ relative = Path(script_path).relative_to(self.project_dir)
56
+ return ".".join(relative.with_suffix("").parts) + f".{name}"
57
+
42
58
  @staticmethod
43
59
  def _infer_type(script: Script, line: int, column: int) -> str:
44
60
  """Tries to infer the type at a given position using Jedi."""
@@ -97,6 +113,44 @@ class SymbolTableBuilder:
97
113
  except Exception:
98
114
  return None, False
99
115
 
116
+ @staticmethod
117
+ def _callee_anchor(node: ast.Call) -> Tuple[int, int]:
118
+ """Position of the callee *name* for Jedi inference.
119
+
120
+ An ``ast.Call``'s own ``lineno``/``col_offset`` is the first
121
+ character of the whole call expression — for an attribute call
122
+ ``receiver.method(...)`` that is the receiver token, and Jedi
123
+ would infer the receiver's type instead of the invoked method
124
+ (issue #80). Anchor attribute calls inside the attribute name —
125
+ its last character, so one-character names stay in range; other
126
+ callee shapes keep the call-expression start.
127
+ """
128
+ func_expr = node.func
129
+ if isinstance(func_expr, ast.Attribute):
130
+ return func_expr.end_lineno, func_expr.end_col_offset - 1
131
+ return node.lineno, node.col_offset
132
+
133
+ @staticmethod
134
+ def _infer_call_return_type(script: Script, line: int, column: int) -> Optional[str]:
135
+ """Inferred type of the call's *result*, not of the callee itself.
136
+
137
+ ``Script.infer`` at the callee name yields the function/class
138
+ being called; executing that definition yields what the call
139
+ evaluates to — a function's inferred return type, or the
140
+ instance for a constructor call. Returns ``None`` when Jedi
141
+ can't tell, so an unknown stays absent instead of masquerading
142
+ as the callee's own name.
143
+ """
144
+ try:
145
+ definitions = script.infer(line=line, column=column)
146
+ if definitions:
147
+ results = definitions[0].execute()
148
+ if results:
149
+ return results[0].name
150
+ except Exception:
151
+ pass
152
+ return None
153
+
100
154
  def build_pymodule_from_file(self, py_file: Path) -> PyModule:
101
155
  """Builds a PyModule from a Python file.
102
156
 
@@ -203,10 +257,10 @@ class SymbolTableBuilder:
203
257
  definitions = script.goto(line=start_line, column=child.col_offset)
204
258
  signature = next(
205
259
  (d.full_name for d in definitions if d.type == "class"),
206
- f"{Path(script.path).relative_to(self.project_dir).__str__().replace('/', '.').replace('.py', '')}.{class_name}"
260
+ self._fallback_signature(script.path, class_name),
207
261
  )
208
262
  except Exception:
209
- signature = f"{Path(script.path).relative_to(self.project_dir).__str__().replace('/', '.').replace('.py', '')}.{class_name}"
263
+ signature = self._fallback_signature(script.path, class_name)
210
264
  py_class = (
211
265
  PyClass.builder()
212
266
  .name(class_name)
@@ -258,8 +312,7 @@ class SymbolTableBuilder:
258
312
 
259
313
  # If Jedi didn't provide a signature, build one relative to project_dir
260
314
  if not signature:
261
- relative_path = Path(script.path).relative_to(self.project_dir)
262
- signature = f"{str(relative_path).replace('/', '.').replace('.py', '')}.{method_name}"
315
+ signature = self._fallback_signature(script.path, method_name)
263
316
  py_callable = (
264
317
  PyCallable.builder()
265
318
  .name(method_name) # Use the actual method name, not the full signature
@@ -380,6 +433,7 @@ class SymbolTableBuilder:
380
433
  script, target.lineno, target.col_offset
381
434
  )
382
435
  )
436
+ .initializer(ast.unparse(stmt.value) if stmt.value else None)
383
437
  .start_line(getattr(target, "lineno", -1))
384
438
  .end_line(getattr(stmt, "end_lineno", stmt.lineno))
385
439
  .build()
@@ -398,6 +452,7 @@ class SymbolTableBuilder:
398
452
  script, target.lineno, target.col_offset
399
453
  )
400
454
  )
455
+ .initializer(ast.unparse(stmt.value) if stmt.value else None)
401
456
  .start_line(getattr(target, "lineno", -1))
402
457
  .end_line(getattr(stmt, "end_lineno", stmt.lineno))
403
458
  .build()
@@ -588,10 +643,11 @@ class SymbolTableBuilder:
588
643
  func_expr = node.func
589
644
 
590
645
  method_name = "<unknown>"
646
+ anchor_line, anchor_col = self._callee_anchor(node)
591
647
  callee_signature, is_constructor = self._infer_callee(
592
- script, node.lineno, node.col_offset
648
+ script, anchor_line, anchor_col
593
649
  )
594
- return_type = self._infer_type(script, node.lineno, node.col_offset)
650
+ return_type = self._infer_call_return_type(script, anchor_line, anchor_col)
595
651
 
596
652
  receiver_expr = None
597
653
  receiver_type = None
@@ -604,18 +660,26 @@ class SymbolTableBuilder:
604
660
  elif isinstance(func_expr, ast.Name):
605
661
  method_name = func_expr.id
606
662
 
607
- argument_types = [
608
- self._infer_type(script, arg.lineno, arg.col_offset)
609
- or type(arg).__name__
663
+ arguments = [
664
+ PyCallArgument(
665
+ ast_kind=type(arg).__name__,
666
+ inferred_type=self._infer_type(script, arg.lineno, arg.col_offset),
667
+ )
610
668
  for arg in node.args
611
669
  ]
612
670
 
671
+ # Legacy field, derived from the structured arguments above rather
672
+ # than re-inferring: byte-identical to the old
673
+ # `self._infer_type(...) or type(arg).__name__` per argument.
674
+ argument_types = [a.inferred_type or a.ast_kind for a in arguments]
675
+
613
676
  call_sites.append(
614
677
  PyCallsite.builder()
615
678
  .method_name(method_name)
616
679
  .receiver_expr(receiver_expr)
617
680
  .receiver_type(receiver_type)
618
681
  .argument_types(argument_types)
682
+ .arguments(arguments)
619
683
  .return_type(return_type)
620
684
  .callee_signature(callee_signature)
621
685
  .is_constructor_call(is_constructor)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: codeanalyzer-python
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Static Analysis on Python source code using Jedi, CodeQL and Treesitter — emits analysis.json or a Neo4j property graph.
5
5
  Author-email: Rahul Krishna <i.m.ralk@gmail.com>
6
6
  License-File: LICENSE
@@ -12,12 +12,7 @@ Requires-Dist: msgpack<1.0.7,>=1.0.0; python_version < '3.11'
12
12
  Requires-Dist: msgpack<2.0.0,>=1.0.7; python_version >= '3.11'
13
13
  Requires-Dist: networkx<3.2.0,>=2.6.0; python_version < '3.11'
14
14
  Requires-Dist: networkx<4.0.0,>=3.0.0; python_version >= '3.11'
15
- Requires-Dist: numpy<1.24.0,>=1.21.0; python_version < '3.11'
16
- Requires-Dist: numpy<2.0.0,>=1.24.0; python_version >= '3.11' and python_version < '3.12'
17
- Requires-Dist: numpy<2.0.0,>=1.26.0; python_version >= '3.12'
18
15
  Requires-Dist: packaging>=25.0
19
- Requires-Dist: pandas<2.0.0,>=1.3.0; python_version < '3.11'
20
- Requires-Dist: pandas<3.0.0,>=2.0.0; python_version >= '3.11'
21
16
  Requires-Dist: pycg>=0.0.6
22
17
  Requires-Dist: pydantic<2.0.0,>=1.8.0; python_version < '3.11'
23
18
  Requires-Dist: pydantic<3.0.0,>=2.0.0; python_version >= '3.11'
@@ -181,19 +176,22 @@ $ canpy --help
181
176
  Static Analysis on Python source code using Jedi, PyCG and Tree sitter.
182
177
 
183
178
  ╭─ Options ────────────────────────────────────────────────────────────────────╮
184
- │ --input -i PATH Path to the
179
+ │ --version Show the canpy
180
+ │ version and │
181
+ │ exit. │
182
+ │ --input -i <path> Path to the │
185
183
  │ project root │
186
184
  │ directory (not │
187
185
  │ required for │
188
186
  │ --emit schema). │
189
- │ --output -o PATH Output directory │
187
+ │ --output -o <path> Output directory │
190
188
  │ for artifacts. │
191
- │ --format -f [json|msgpack] Output format │
189
+ │ --format -f <json|msgpack> Output format │
192
190
  │ for --emit json: │
193
191
  │ json or msgpack. │
194
192
  │ [default: json] │
195
- │ --emit [json|neo4j|sche Output target: │
196
- │ ma] json │
193
+ │ --emit <json|neo4j|sche Output target: │
194
+ │ ma> json │
197
195
  │ (analysis.json, │
198
196
  │ default) | neo4j │
199
197
  │ (graph.cypher or │
@@ -203,13 +201,13 @@ $ canpy --help
203
201
  │ schema.json │
204
202
  │ contract). │
205
203
  │ [default: json] │
206
- │ --app-name TEXT Logical │
204
+ │ --app-name <str> Logical │
207
205
  │ application name │
208
206
  │ for the graph │
209
207
  │ :PyApplication │
210
208
  │ anchor (default: │
211
209
  │ input dir name). │
212
- │ --neo4j-uri TEXT Push the graph │
210
+ │ --neo4j-uri <str> Push the graph │
213
211
  │ to a live Neo4j │
214
212
  │ over Bolt │
215
213
  │ (incremental); │
@@ -217,11 +215,11 @@ $ canpy --help
217
215
  │ graph.cypher. │
218
216
  │ [env var: │
219
217
  │ NEO4J_URI] │
220
- │ --neo4j-user TEXT Neo4j username. │
218
+ │ --neo4j-user <str> Neo4j username. │
221
219
  │ [env var: │
222
220
  │ NEO4J_USERNAME] │
223
221
  │ [default: neo4j] │
224
- │ --neo4j-password TEXT Neo4j password. │
222
+ │ --neo4j-password <str> Neo4j password. │
225
223
  │ Prefer the env │
226
224
  │ var over the │
227
225
  │ flag (the flag │
@@ -231,12 +229,12 @@ $ canpy --help
231
229
  │ [env var: │
232
230
  │ NEO4J_PASSWORD] │
233
231
  │ [default: neo4j] │
234
- │ --neo4j-database TEXT Neo4j database │
232
+ │ --neo4j-database <str> Neo4j database │
235
233
  │ name (default: │
236
234
  │ server default). │
237
235
  │ [env var: │
238
236
  │ NEO4J_DATABASE] │
239
- │ --analysis-level -a INTEGER RANGE Analysis depth: │
237
+ │ --analysis-level -a <int range> Analysis depth: │
240
238
  │ [1<=x<=2] 1=symbol │
241
239
  │ table+Jedi call │
242
240
  │ graph, 2=+PyCG │
@@ -266,12 +264,12 @@ $ canpy --help
266
264
  │ environment │
267
265
  │ instead. │
268
266
  │ [default: venv] │
269
- │ --file-name PATH Analyze only the │
267
+ │ --file-name <path> Analyze only the │
270
268
  │ specified file │
271
269
  │ (relative to │
272
270
  │ input │
273
271
  │ directory). │
274
- │ --cache-dir -c PATH Directory to │
272
+ │ --cache-dir -c <path> Directory to │
275
273
  │ store analysis │
276
274
  │ cache. Defaults │
277
275
  │ to │
@@ -285,7 +283,7 @@ $ canpy --help
285
283
  │ retained. │
286
284
  │ [default: │
287
285
  │ keep-cache] │
288
- │ -v INTEGER Increase │
286
+ │ -v <int> Increase │
289
287
  │ verbosity: -v, │
290
288
  │ -vv, -vvv │
291
289
  │ [default: 0] │
@@ -312,7 +310,7 @@ $ canpy --help
312
310
  │ Jedi-only edges. │
313
311
  │ [default: │
314
312
  │ no-pycg-shard] │
315
- │ --pycg-shard-cei… INTEGER RANGE Maximum files │
313
+ │ --pycg-shard-cei… <int range> Maximum files │
316
314
  │ [x>=1] per shard when │
317
315
  │ --pycg-shard is │
318
316
  │ active (default │
@@ -334,7 +332,7 @@ $ canpy --help
334
332
  │ heavy import │
335
333
  │ graphs. │
336
334
  │ [default: 100] │
337
- │ --pycg-shard-tim… INTEGER RANGE Per-shard │
335
+ │ --pycg-shard-tim… <int range> Per-shard │
338
336
  │ [x>=0] wall-clock │
339
337
  │ timeout in │
340
338
  │ seconds when │
@@ -362,7 +360,7 @@ $ canpy --help
362
360
  │ ignored on │
363
361
  │ Windows. │
364
362
  │ [default: 120] │
365
- │ --pycg-shard-str… [jedi|package] How --pycg-shard │
363
+ │ --pycg-shard-str… <jedi|package> How --pycg-shard │
366
364
  │ groups files │
367
365
  │ (level 2 only). │
368
366
  │ 'jedi' (default) │
@@ -384,7 +382,7 @@ $ canpy --help
384
382
  │ one-shard-per-p… │
385
383
  │ grouping. │
386
384
  │ [default: jedi] │
387
- │ --pycg-max-iter INTEGER RANGE Cap on PyCG's │
385
+ │ --pycg-max-iter <int range> Cap on PyCG's │
388
386
  │ [x>=-1] fixpoint passes │
389
387
  │ per │
390
388
  │ shard/project │
@@ -1,23 +1,23 @@
1
1
  codeanalyzer/__init__.py,sha256=BZ3Kuwl-F_F-8H8cepLnVJ4Ku4NNUjjqg0Y6ujPQSsI,108
2
- codeanalyzer/__main__.py,sha256=-los-YRXrmZsISNrWh8EjgUcUSkUbSGqEX7yadZuvB8,11920
3
- codeanalyzer/core.py,sha256=rUKEa4jAbQFtvAnkDKinBGznLpb6VquULLlMVoFjMTU,29533
2
+ codeanalyzer/__main__.py,sha256=GOGc6j-euSeRZbUQgfZykQClT32WNxcStKoO96s-VSY,12790
3
+ codeanalyzer/core.py,sha256=BCH5OynbXrnItPvYhcpAChcBtkXdQIMbH_fyFGCxITU,31185
4
+ codeanalyzer/provenance.py,sha256=DT-DqwdVwO7g-GyK3sC0_4vDNCUjZYEY1fyYCt-7VRM,2306
4
5
  codeanalyzer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
6
  codeanalyzer/config/__init__.py,sha256=9XBxAn1oWGRuhg3bEBUuVGs3hFNXEAKrr-Ce7tq9a2k,61
6
7
  codeanalyzer/config/config.py,sha256=ZiKzc5uEUCIvih58-6BDtLLI1hPij41wGQjBcj9KNQM,188
7
8
  codeanalyzer/jedi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
9
  codeanalyzer/jedi/jedi.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- codeanalyzer/neo4j/__init__.py,sha256=AcbFNAMuXwkMFWH4h_HCmla6PCKTF3Xe5yNqg8F_kYk,1575
10
+ codeanalyzer/neo4j/__init__.py,sha256=8Ei6uThTOqmul6GhnKoGOjLXJZbyDwF5Jo-8I8NxnAk,1574
10
11
  codeanalyzer/neo4j/bolt.py,sha256=-IFDb_d67IkGwxvmmbLNI6AvSQHY-XsutI3szibWidw,9635
11
- codeanalyzer/neo4j/catalog.py,sha256=VQWtwcueck2Ug_cu8sitvAoRwbVZ-6KRiexRN8q1Wak,7418
12
12
  codeanalyzer/neo4j/cypher.py,sha256=2zIWXA1AADrwCMhSTeqKjEXRgBjbob6o3bme_cwLu0s,5024
13
- codeanalyzer/neo4j/emit.py,sha256=WtCndN6mA6PIzfzdgv9Xc5S5WP4rHUXCtB_r3G16rkg,3101
14
- codeanalyzer/neo4j/project.py,sha256=2GDHkFjmWibrpMaOiDMo21K6Lnj3aefl2QPCM7ANA7g,12485
13
+ codeanalyzer/neo4j/emit.py,sha256=NZL5BVY1Fb32igH22986_cUFUIgHNWJHbii2bfX2E3M,3099
14
+ codeanalyzer/neo4j/project.py,sha256=YMVtF1GjLZYwnhFl-7fy9Qj7fpGgd5aEGOp0JMF3VAY,14513
15
15
  codeanalyzer/neo4j/rows.py,sha256=5xI3X-l-vwPe_gmKYUg7VuUQARcOlVAZnGmiQr9QyRk,7326
16
- codeanalyzer/neo4j/schema.py,sha256=5xz8cZVuL73GAF-vs9QtN2TuKxIty0rHEe68nwQmLTY,2136
16
+ codeanalyzer/neo4j/schema.py,sha256=tZjnpIFdTV3-GB1x2zTbDMxQOH16GqELYCf2cR2XhgU,8765
17
17
  codeanalyzer/options/__init__.py,sha256=Ki4qhHFqpyuUWVsntO-NYJMVWrkeFOzPW4nQ7oxiUVI,155
18
18
  codeanalyzer/options/options.py,sha256=5w3DZYlAv-0LVPavF1P5EEJp8XBRrSXidbDusdbXQAo,1976
19
19
  codeanalyzer/schema/__init__.py,sha256=cLPjvowrnz8xzi7tZAsKQeIOjdOKRGHy4I7wbG0jHk8,2024
20
- codeanalyzer/schema/py_schema.py,sha256=6hISjVzoWTq-SHLjT7FsL__uzHT5DY0NYqlpJTLsc9s,12314
20
+ codeanalyzer/schema/py_schema.py,sha256=yYKkg3ufNDteNvtiPiRJQz-pzK5NnnnNldhXcW7p2RI,13274
21
21
  codeanalyzer/semantic_analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  codeanalyzer/semantic_analysis/call_graph.py,sha256=H3IkfGp1VCkDxI75JPyLSSA-wOaJp_I-NYPsjCY04Bg,11185
23
23
  codeanalyzer/semantic_analysis/pycg/__init__.py,sha256=Lsgz25iFM_RGGu_i2psY-LN-KwvYIZQPnKgRPL1JsjU,928
@@ -26,13 +26,14 @@ codeanalyzer/semantic_analysis/pycg/pycg_exceptions.py,sha256=n4tRderrSYw9cUYgR2
26
26
  codeanalyzer/semantic_analysis/pycg/shard_planner.py,sha256=PTXMG9YcrZX9hgnlnIlpQx6xmWkizP3aQrf0elcrnmw,15865
27
27
  codeanalyzer/syntactic_analysis/__init__.py,sha256=EUQkJEh6wHjWx2qTTKbTbUgwSbfKeNieKHNy7RknVXA,476
28
28
  codeanalyzer/syntactic_analysis/exceptions.py,sha256=whs_n0vIu655Jkk1a7iOoXY6iIca4pZqJnU40V9Ejaw,537
29
- codeanalyzer/syntactic_analysis/symbol_table_builder.py,sha256=zmHFt8pN50jG-Ex4fnisvbLmn1XaW05jwbV_xSG4qfU,38177
29
+ codeanalyzer/syntactic_analysis/import_resolver.py,sha256=Q8noZwSdDNt4P4NMqDTB9FaS-M0_d4ASK9GnJop9MDI,2751
30
+ codeanalyzer/syntactic_analysis/symbol_table_builder.py,sha256=hQOEk2qRYM9Y_UoejNbZFCWSkDjBa-vRcU3kPURrMNc,41148
30
31
  codeanalyzer/utils/__init__.py,sha256=hC6VWdR5rerSqBxzu9KQHTASWqwrrYJv-CMDwrTlzkc,137
31
32
  codeanalyzer/utils/logging.py,sha256=Fw0tattPAOMs3o0JMjjXhRVLIF64f-SCcygUXF9jqeg,904
32
33
  codeanalyzer/utils/progress_bar.py,sha256=C9JtzVdd10lIxTv-KA6PebqjKWueC_vMGwVzAtHuHIw,2818
33
- codeanalyzer_python-0.3.0.dist-info/METADATA,sha256=l7oUfSjJiPF1mvfQQOmm6JyzIWOBveKKp8xHJF0dw-E,33777
34
- codeanalyzer_python-0.3.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
35
- codeanalyzer_python-0.3.0.dist-info/entry_points.txt,sha256=v4Vux0Nnx7sOntVk_CH7W9RX6SkIkvR1FQYq73oVlCQ,105
36
- codeanalyzer_python-0.3.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
37
- codeanalyzer_python-0.3.0.dist-info/licenses/NOTICE,sha256=YU0Z9NDWqKY-2jfFcbxeZ6fbnzz0oZeKmnUcO8a-bcQ,901
38
- codeanalyzer_python-0.3.0.dist-info/RECORD,,
34
+ codeanalyzer_python-0.3.2.dist-info/METADATA,sha256=oX5F_aEJQSK9cZWGnI9rqSbNXUQQyPZSZSRuXRNEJ20,33695
35
+ codeanalyzer_python-0.3.2.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
36
+ codeanalyzer_python-0.3.2.dist-info/entry_points.txt,sha256=v4Vux0Nnx7sOntVk_CH7W9RX6SkIkvR1FQYq73oVlCQ,105
37
+ codeanalyzer_python-0.3.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
38
+ codeanalyzer_python-0.3.2.dist-info/licenses/NOTICE,sha256=YU0Z9NDWqKY-2jfFcbxeZ6fbnzz0oZeKmnUcO8a-bcQ,901
39
+ codeanalyzer_python-0.3.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.30.1
2
+ Generator: hatchling 1.32.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any