py-flow-mapper 0.1.0b5.dev0__tar.gz → 0.1.0b7.dev0__tar.gz

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 (16) hide show
  1. {py_flow_mapper-0.1.0b5.dev0/src/py_flow_mapper.egg-info → py_flow_mapper-0.1.0b7.dev0}/PKG-INFO +1 -1
  2. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/setup.py +1 -1
  3. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/src/py_flow_mapper/__init__.py +1 -1
  4. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/src/py_flow_mapper/analyzer.py +12 -9
  5. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/src/py_flow_mapper/mermaid_generator.py +38 -24
  6. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/src/py_flow_mapper/utils.py +1 -1
  7. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0/src/py_flow_mapper.egg-info}/PKG-INFO +1 -1
  8. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/LICENSE +0 -0
  9. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/README.md +0 -0
  10. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/setup.cfg +0 -0
  11. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/src/py_flow_mapper/cli.py +0 -0
  12. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/src/py_flow_mapper.egg-info/SOURCES.txt +0 -0
  13. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/src/py_flow_mapper.egg-info/dependency_links.txt +0 -0
  14. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/src/py_flow_mapper.egg-info/entry_points.txt +0 -0
  15. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/src/py_flow_mapper.egg-info/requires.txt +0 -0
  16. {py_flow_mapper-0.1.0b5.dev0 → py_flow_mapper-0.1.0b7.dev0}/src/py_flow_mapper.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py-flow-mapper
3
- Version: 0.1.0b5.dev0
3
+ Version: 0.1.0b7.dev0
4
4
  Summary: Python project analyzer and visualization tool
5
5
  Home-page: https://github.com/ArunKoundinya/py-flow-mapper
6
6
  Author: Arun Koundinya Parasa
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="py-flow-mapper",
8
- version="0.1.0b5.dev",
8
+ version="0.1.0b7.dev",
9
9
  author="Arun Koundinya Parasa",
10
10
  author_email="parasa.arunkoundinya@gmail.com",
11
11
  description="Python project analyzer and visualization tool",
@@ -2,7 +2,7 @@
2
2
  PyFlowMapper - A Python project analyzer and visualization tool.
3
3
  """
4
4
 
5
- __version__ = "0.1.0b5.dev"
5
+ __version__ = "0.1.0b7.dev"
6
6
  __author__ = "Arun Koundinya Parasa"
7
7
  __description__ = "Analyze Python projects and generate dependency graphs"
8
8
  __github__ = "https://github.com/ArunKoundinya/py-flow-mapper"
@@ -521,21 +521,24 @@ class DataFlowAnalyzer(ast.NodeVisitor):
521
521
 
522
522
  def visit_Assign(self, node):
523
523
  """Track assignments of function return values."""
524
- if node.targets and isinstance(node.targets[0], ast.Name):
524
+ # Get the variable name being assigned to
525
+ if isinstance(node.targets[0], ast.Name):
525
526
  var_name = node.targets[0].id
526
-
527
+
528
+ # Check if the value is a function call
527
529
  if isinstance(node.value, ast.Call):
528
530
  call_info = self._extract_call_info(node.value)
529
531
  if call_info:
530
- func_name = call_info["func_name"]
531
-
532
- if func_name not in self.calls:
533
- self.calls.append(func_name)
534
- self.return_assignments.setdefault(var_name, []).append(func_name)
535
-
532
+ self.calls.append(call_info['func_name'])
533
+ if var_name not in self.return_assignments:
534
+ self.return_assignments[var_name] = []
535
+ self.return_assignments[var_name].append(call_info['func_name'])
536
+
537
+ # Check if the value is a Name (could be a variable holding a return value)
536
538
  elif isinstance(node.value, ast.Name):
539
+ # Track variable assignments for data flow
537
540
  pass
538
-
541
+
539
542
  self.generic_visit(node)
540
543
 
541
544
  def visit_Call(self, node):
@@ -71,16 +71,23 @@ class MermaidGenerator:
71
71
  return ""
72
72
  return ",".join(sorted(set(cleaned)))
73
73
 
74
+ current_module_ctx = ""
74
75
 
75
- def module_import_mapping(mod: str) -> dict:
76
- return (modules.get(mod, {}) or {}).get("import_mapping", {}) or {}
76
+ def module_import_mapping() -> dict:
77
+ return (modules.get(current_module_ctx, {}) or {}).get("import_mapping", {}) or {}
77
78
 
78
- def external_root_name(call_name: str, current_module: str) -> str:
79
+ def external_root_name(call_name: str) -> str:
79
80
  if not call_name:
80
81
  return ""
82
+
81
83
  root = call_name.split(".")[0]
82
- imp_map = module_import_mapping(current_module)
84
+
85
+ # module-specific import mapping (from analyzer)
86
+ imp_map = module_import_mapping() or {}
87
+
88
+ # merge common aliases (module imports override defaults)
83
89
  merged_map = {**COMMON_ALIAS_MAP, **imp_map}
90
+
84
91
  return merged_map.get(root, root)
85
92
 
86
93
  # Keep the graph clean: ignore common builtins + attribute-noise
@@ -92,35 +99,42 @@ class MermaidGenerator:
92
99
  "Path",
93
100
  }
94
101
 
95
- def keep_external(call_name: str, current_module: str) -> bool:
102
+ def keep_external(call_name: str) -> bool:
96
103
  if not call_name:
97
104
  return False
98
105
 
99
106
  base = short_label(call_name)
100
107
 
101
- root = external_root_name(call_name, current_module)
108
+ # forced external always wins
109
+ root = external_root_name(call_name)
102
110
  if root and root in self.force_external:
103
111
  return True
104
112
  if base in self.force_external:
105
113
  return True
106
114
 
107
- imp_map = module_import_mapping(current_module)
115
+ # NEW: module-specific import mapping
116
+ imp_map = module_import_mapping() or {}
108
117
  if base in imp_map:
109
118
  mapped = imp_map[base]
110
119
  mapped_module = ".".join(mapped.split(".")[:-1])
120
+
111
121
  if mapped_module in modules:
112
122
  return False
123
+
113
124
  return True
114
125
 
126
+ # internal class => NOT external
115
127
  if base in internal_classes:
116
128
  return False
117
129
 
118
130
  if base in NOISY_EXTERNAL:
119
131
  return False
120
132
 
133
+ # dotted calls like obj.method: too noisy at this level
121
134
  if "." in call_name:
122
135
  return False
123
136
 
137
+ # keep CamelCase (tool-ish classes / constructors)
124
138
  return is_camel_case(base)
125
139
 
126
140
  def resolve_internal(call: str, current_module: str) -> str:
@@ -160,7 +174,7 @@ class MermaidGenerator:
160
174
 
161
175
  info = function_map.get(fn_key, {})
162
176
  current_module = info.get("module", "") or ""
163
- #current_module_ctx = current_module
177
+ current_module_ctx = current_module
164
178
  for c in (info.get("calls") or []):
165
179
  target = self._find_function_full_name(c, current_module)
166
180
  if target and target in function_map:
@@ -196,15 +210,15 @@ class MermaidGenerator:
196
210
 
197
211
  for _, info in function_map.items():
198
212
  current_module = info.get("module", "") or ""
199
- #current_module_ctx = current_module
213
+ current_module_ctx = current_module
200
214
 
201
215
  # prefer call_arguments keys
202
216
  call_args = info.get("call_arguments", {}) or {}
203
217
  for callee in call_args.keys():
204
218
  if resolve_internal(callee, current_module):
205
219
  continue
206
- if keep_external(callee, current_module):
207
- external_nodes.add(external_root_name(callee, current_module) or callee)
220
+ if keep_external(callee):
221
+ external_nodes.add(external_root_name(callee) or callee)
208
222
 
209
223
  # scan raw calls for Done + missed externals
210
224
  for callee in (info.get("calls") or []):
@@ -213,8 +227,8 @@ class MermaidGenerator:
213
227
 
214
228
  if resolve_internal(callee, current_module):
215
229
  continue
216
- if keep_external(callee, current_module):
217
- external_nodes.add(external_root_name(callee, current_module) or callee)
230
+ if keep_external(callee):
231
+ external_nodes.add(external_root_name(callee) or callee)
218
232
 
219
233
  if external_nodes or uses_done:
220
234
  lines.append(" subgraph External [External]")
@@ -233,7 +247,7 @@ class MermaidGenerator:
233
247
 
234
248
  info = function_map[caller]
235
249
  current_module = info.get("module", "") or ""
236
- #current_module_ctx = current_module
250
+ current_module_ctx = current_module
237
251
  src = nid(caller)
238
252
 
239
253
  call_args = info.get("call_arguments", {}) or {}
@@ -260,8 +274,8 @@ class MermaidGenerator:
260
274
  else:
261
275
  lines.append(f" {src} --> {nid(target_internal)}")
262
276
  else:
263
- if keep_external(callee, current_module):
264
- ext = external_root_name(callee, current_module) or callee
277
+ if keep_external(callee):
278
+ ext = external_root_name(callee) or callee
265
279
  edge_key = (src, nid("ext:" + callee), label)
266
280
  if edge_key in seen:
267
281
  continue
@@ -280,7 +294,7 @@ class MermaidGenerator:
280
294
  info = function_map[fn]
281
295
  dst = nid(fn)
282
296
  current_module = info.get("module", "") or ""
283
- #current_module_ctx = current_module
297
+ current_module_ctx = current_module
284
298
 
285
299
  return_assignments = info.get("return_assignments", {}) or {}
286
300
  for var_name, producers in return_assignments.items():
@@ -289,20 +303,20 @@ class MermaidGenerator:
289
303
  if internal_p:
290
304
  lines.append(f" {nid(internal_p)} -.->|{var_name}| {dst}")
291
305
  else:
292
- if keep_external(p, current_module):
293
- ext = external_root_name(p,current_module) or p
306
+ if keep_external(p):
307
+ ext = external_root_name(p) or p
294
308
  lines.append(f" {nid('ext:' + ext)} -.->|{var_name}| {dst}")
295
309
 
296
310
  # ---------- pipeline heuristic: external tool A -> external tool B ----------
297
311
  for _, info in function_map.items():
298
312
  current_module = info.get("module", "") or ""
299
- #current_module_ctx = current_module
313
+ current_module_ctx = current_module
300
314
  calls = info.get("calls") or []
301
315
  call_args = info.get("call_arguments") or {}
302
316
 
303
317
  meaningful = []
304
318
  for c in calls:
305
- if c and keep_external(c,current_module) and not resolve_internal(c, current_module):
319
+ if c and keep_external(c) and not resolve_internal(c, current_module):
306
320
  if c not in meaningful:
307
321
  meaningful.append(c)
308
322
 
@@ -311,7 +325,7 @@ class MermaidGenerator:
311
325
 
312
326
  fileish_callee = None
313
327
  for callee, args in call_args.items():
314
- if keep_external(callee, current_module) and any(is_fileish_arg(a) for a in (args or []) if isinstance(a, str)):
328
+ if keep_external(callee) and any(is_fileish_arg(a) for a in (args or []) if isinstance(a, str)):
315
329
  fileish_callee = callee
316
330
  break
317
331
 
@@ -324,8 +338,8 @@ class MermaidGenerator:
324
338
 
325
339
  vars_passed = call_args.get(fileish_callee, []) or []
326
340
  label = normalize_vars(vars_passed) or "value"
327
- prod_ext = external_root_name(producer,current_module) or producer
328
- file_ext = external_root_name(fileish_callee,current_module) or fileish_callee
341
+ prod_ext = external_root_name(producer) or producer
342
+ file_ext = external_root_name(fileish_callee) or fileish_callee
329
343
  lines.append(f" {nid('ext:' + prod_ext)} -->|{label}| {nid('ext:' + file_ext)}")
330
344
 
331
345
  # ---------- Done edge ----------
@@ -130,7 +130,7 @@ def get_project_structure(base_path: Path, exclude_dirs: List[str] = None) -> Di
130
130
  "node_modules", "site-packages",
131
131
  "docs", "doc", "notebooks", ".ipynb_checkpoints",
132
132
  "models", "outputs", "results",
133
- "logs", "tmp", "temp",
133
+ "logs", "tmp", "temp","log","cache"
134
134
  ]
135
135
 
136
136
  exclude_set = set(exclude_dirs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py-flow-mapper
3
- Version: 0.1.0b5.dev0
3
+ Version: 0.1.0b7.dev0
4
4
  Summary: Python project analyzer and visualization tool
5
5
  Home-page: https://github.com/ArunKoundinya/py-flow-mapper
6
6
  Author: Arun Koundinya Parasa