py-flow-mapper 0.1.0b4.dev0__tar.gz → 0.1.0b6.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.0b4.dev0/src/py_flow_mapper.egg-info → py_flow_mapper-0.1.0b6.dev0}/PKG-INFO +1 -1
  2. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/setup.py +1 -1
  3. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/src/py_flow_mapper/__init__.py +1 -1
  4. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/src/py_flow_mapper/analyzer.py +5 -2
  5. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/src/py_flow_mapper/mermaid_generator.py +56 -41
  6. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/src/py_flow_mapper/utils.py +1 -1
  7. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0/src/py_flow_mapper.egg-info}/PKG-INFO +1 -1
  8. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/LICENSE +0 -0
  9. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/README.md +0 -0
  10. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/setup.cfg +0 -0
  11. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/src/py_flow_mapper/cli.py +0 -0
  12. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/src/py_flow_mapper.egg-info/SOURCES.txt +0 -0
  13. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/src/py_flow_mapper.egg-info/dependency_links.txt +0 -0
  14. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/src/py_flow_mapper.egg-info/entry_points.txt +0 -0
  15. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.dev0}/src/py_flow_mapper.egg-info/requires.txt +0 -0
  16. {py_flow_mapper-0.1.0b4.dev0 → py_flow_mapper-0.1.0b6.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.0b4.dev0
3
+ Version: 0.1.0b6.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.0b4.dev",
8
+ version="0.1.0b6.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.0b4.dev"
5
+ __version__ = "0.1.0b6.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"
@@ -527,13 +527,16 @@ class DataFlowAnalyzer(ast.NodeVisitor):
527
527
  if isinstance(node.value, ast.Call):
528
528
  call_info = self._extract_call_info(node.value)
529
529
  if call_info:
530
- self.return_assignments.setdefault(var_name, []).append(call_info["func_name"])
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)
531
535
 
532
536
  elif isinstance(node.value, ast.Name):
533
537
  pass
534
538
 
535
539
  self.generic_visit(node)
536
-
537
540
 
538
541
  def visit_Call(self, node):
539
542
  call_info = self._extract_call_info(node)
@@ -71,23 +71,16 @@ class MermaidGenerator:
71
71
  return ""
72
72
  return ",".join(sorted(set(cleaned)))
73
73
 
74
- current_module_ctx = ""
75
74
 
76
- def module_import_mapping() -> dict:
77
- return (modules.get(current_module_ctx, {}) or {}).get("import_mapping", {}) or {}
75
+ def module_import_mapping(mod: str) -> dict:
76
+ return (modules.get(mod, {}) or {}).get("import_mapping", {}) or {}
78
77
 
79
- def external_root_name(call_name: str) -> str:
78
+ def external_root_name(call_name: str, current_module: str) -> str:
80
79
  if not call_name:
81
80
  return ""
82
-
83
81
  root = call_name.split(".")[0]
84
-
85
- # module-specific import mapping (from analyzer)
86
- imp_map = module_import_mapping() or {}
87
-
88
- # merge common aliases (module imports override defaults)
82
+ imp_map = module_import_mapping(current_module)
89
83
  merged_map = {**COMMON_ALIAS_MAP, **imp_map}
90
-
91
84
  return merged_map.get(root, root)
92
85
 
93
86
  # Keep the graph clean: ignore common builtins + attribute-noise
@@ -99,52 +92,74 @@ class MermaidGenerator:
99
92
  "Path",
100
93
  }
101
94
 
102
- def keep_external(call_name: str) -> bool:
95
+ def keep_external(call_name: str, current_module: str) -> bool:
103
96
  if not call_name:
104
97
  return False
105
98
 
106
99
  base = short_label(call_name)
107
100
 
108
- # forced external always wins
109
- root = external_root_name(call_name)
101
+ root = external_root_name(call_name, current_module)
110
102
  if root and root in self.force_external:
111
103
  return True
112
104
  if base in self.force_external:
113
105
  return True
114
106
 
115
- # NEW: module-specific import mapping
116
- imp_map = module_import_mapping() or {}
107
+ imp_map = module_import_mapping(current_module)
117
108
  if base in imp_map:
118
109
  mapped = imp_map[base]
119
110
  mapped_module = ".".join(mapped.split(".")[:-1])
120
-
121
111
  if mapped_module in modules:
122
112
  return False
123
-
124
113
  return True
125
114
 
126
- # internal class => NOT external
127
115
  if base in internal_classes:
128
116
  return False
129
117
 
130
118
  if base in NOISY_EXTERNAL:
131
119
  return False
132
120
 
133
- # dotted calls like obj.method: too noisy at this level
134
121
  if "." in call_name:
135
122
  return False
136
123
 
137
- # keep CamelCase (tool-ish classes / constructors)
138
124
  return is_camel_case(base)
139
125
 
140
126
  def resolve_internal(call: str, current_module: str) -> str:
141
- # First try your normal resolver
127
+ call = call or ""
128
+
129
+ # 1) existing resolver
142
130
  target = self._find_function_full_name(call, current_module)
143
131
  if target and target in internal_funcs:
144
132
  return target
145
133
 
146
- # NEW: if call is like "obj.method", try to map by method name
147
- if "." in (call or ""):
134
+ imp_map = module_import_mapping(current_module)
135
+
136
+ # 2) If call is a bare name imported via "from X import name"
137
+ # import_mapping might contain: name -> "pkg.mod.name"
138
+ if call in imp_map:
139
+ mapped = imp_map[call]
140
+ if mapped in internal_funcs:
141
+ return mapped
142
+ # if mapped is "pkg.mod.name", try resolving it (or its tail)
143
+ target = self._find_function_full_name(mapped, current_module)
144
+ if target and target in internal_funcs:
145
+ return target
146
+
147
+ # 3) If call is "alias.something", rewrite alias using import_mapping
148
+ # import_mapping might contain: alias -> "pkg.mod"
149
+ if "." in call:
150
+ root, rest = call.split(".", 1)
151
+ if root in imp_map:
152
+ mapped_root = imp_map[root]
153
+ # If mapping is to a specific object like "pkg.mod.func",
154
+ # treat it as root too (best-effort)
155
+ candidate = f"{mapped_root}.{rest}"
156
+ if candidate in internal_funcs:
157
+ return candidate
158
+ target = self._find_function_full_name(candidate, current_module)
159
+ if target and target in internal_funcs:
160
+ return target
161
+
162
+ # 4) your existing "obj.method" heuristic
148
163
  method = call.split(".")[-1]
149
164
  matches = [k for k in internal_funcs if k.endswith("." + method)]
150
165
  if len(matches) == 1:
@@ -174,7 +189,7 @@ class MermaidGenerator:
174
189
 
175
190
  info = function_map.get(fn_key, {})
176
191
  current_module = info.get("module", "") or ""
177
- current_module_ctx = current_module
192
+ #current_module_ctx = current_module
178
193
  for c in (info.get("calls") or []):
179
194
  target = self._find_function_full_name(c, current_module)
180
195
  if target and target in function_map:
@@ -210,15 +225,15 @@ class MermaidGenerator:
210
225
 
211
226
  for _, info in function_map.items():
212
227
  current_module = info.get("module", "") or ""
213
- current_module_ctx = current_module
228
+ #current_module_ctx = current_module
214
229
 
215
230
  # prefer call_arguments keys
216
231
  call_args = info.get("call_arguments", {}) or {}
217
232
  for callee in call_args.keys():
218
233
  if resolve_internal(callee, current_module):
219
234
  continue
220
- if keep_external(callee):
221
- external_nodes.add(external_root_name(callee) or callee)
235
+ if keep_external(callee, current_module):
236
+ external_nodes.add(external_root_name(callee, current_module) or callee)
222
237
 
223
238
  # scan raw calls for Done + missed externals
224
239
  for callee in (info.get("calls") or []):
@@ -227,8 +242,8 @@ class MermaidGenerator:
227
242
 
228
243
  if resolve_internal(callee, current_module):
229
244
  continue
230
- if keep_external(callee):
231
- external_nodes.add(external_root_name(callee) or callee)
245
+ if keep_external(callee, current_module):
246
+ external_nodes.add(external_root_name(callee, current_module) or callee)
232
247
 
233
248
  if external_nodes or uses_done:
234
249
  lines.append(" subgraph External [External]")
@@ -247,7 +262,7 @@ class MermaidGenerator:
247
262
 
248
263
  info = function_map[caller]
249
264
  current_module = info.get("module", "") or ""
250
- current_module_ctx = current_module
265
+ #current_module_ctx = current_module
251
266
  src = nid(caller)
252
267
 
253
268
  call_args = info.get("call_arguments", {}) or {}
@@ -274,8 +289,8 @@ class MermaidGenerator:
274
289
  else:
275
290
  lines.append(f" {src} --> {nid(target_internal)}")
276
291
  else:
277
- if keep_external(callee):
278
- ext = external_root_name(callee) or callee
292
+ if keep_external(callee, current_module):
293
+ ext = external_root_name(callee, current_module) or callee
279
294
  edge_key = (src, nid("ext:" + callee), label)
280
295
  if edge_key in seen:
281
296
  continue
@@ -294,7 +309,7 @@ class MermaidGenerator:
294
309
  info = function_map[fn]
295
310
  dst = nid(fn)
296
311
  current_module = info.get("module", "") or ""
297
- current_module_ctx = current_module
312
+ #current_module_ctx = current_module
298
313
 
299
314
  return_assignments = info.get("return_assignments", {}) or {}
300
315
  for var_name, producers in return_assignments.items():
@@ -303,20 +318,20 @@ class MermaidGenerator:
303
318
  if internal_p:
304
319
  lines.append(f" {nid(internal_p)} -.->|{var_name}| {dst}")
305
320
  else:
306
- if keep_external(p):
307
- ext = external_root_name(p) or p
321
+ if keep_external(p, current_module):
322
+ ext = external_root_name(p,current_module) or p
308
323
  lines.append(f" {nid('ext:' + ext)} -.->|{var_name}| {dst}")
309
324
 
310
325
  # ---------- pipeline heuristic: external tool A -> external tool B ----------
311
326
  for _, info in function_map.items():
312
327
  current_module = info.get("module", "") or ""
313
- current_module_ctx = current_module
328
+ #current_module_ctx = current_module
314
329
  calls = info.get("calls") or []
315
330
  call_args = info.get("call_arguments") or {}
316
331
 
317
332
  meaningful = []
318
333
  for c in calls:
319
- if c and keep_external(c) and not resolve_internal(c, current_module):
334
+ if c and keep_external(c,current_module) and not resolve_internal(c, current_module):
320
335
  if c not in meaningful:
321
336
  meaningful.append(c)
322
337
 
@@ -325,7 +340,7 @@ class MermaidGenerator:
325
340
 
326
341
  fileish_callee = None
327
342
  for callee, args in call_args.items():
328
- if keep_external(callee) and any(is_fileish_arg(a) for a in (args or []) if isinstance(a, str)):
343
+ if keep_external(callee, current_module) and any(is_fileish_arg(a) for a in (args or []) if isinstance(a, str)):
329
344
  fileish_callee = callee
330
345
  break
331
346
 
@@ -338,8 +353,8 @@ class MermaidGenerator:
338
353
 
339
354
  vars_passed = call_args.get(fileish_callee, []) or []
340
355
  label = normalize_vars(vars_passed) or "value"
341
- prod_ext = external_root_name(producer) or producer
342
- file_ext = external_root_name(fileish_callee) or fileish_callee
356
+ prod_ext = external_root_name(producer,current_module) or producer
357
+ file_ext = external_root_name(fileish_callee,current_module) or fileish_callee
343
358
  lines.append(f" {nid('ext:' + prod_ext)} -->|{label}| {nid('ext:' + file_ext)}")
344
359
 
345
360
  # ---------- 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.0b4.dev0
3
+ Version: 0.1.0b6.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