1+ import ast
12import socket
23from collections import defaultdict
34from os import listdir , path
4- from typing import Any , Callable , Dict , List , Optional , Tuple , TypeVar , Union
5+ from typing import Any , Callable , Dict , List , Optional , Set , Tuple , TypeVar , Union
56
67from pyinfra import logger
78from pyinfra .api .inventory import Inventory
@@ -55,6 +56,31 @@ def _is_inventory_group(key: str, value: Any):
5556 return True
5657
5758
59+ def _get_imported_names (filename : str ) -> Set [str ]:
60+ """
61+ Return the set of names bound by ``import`` / ``from ... import`` statements
62+ in ``filename``. Used to keep those names out of the resulting group data dict
63+ (issue #1297) so that e.g. ``from pyinfra import inventory`` in a group data
64+ file does not end up as a piece of group data and break ``debug-inventory``.
65+ """
66+ with open (filename , "r" , encoding = "utf-8" ) as f :
67+ tree = ast .parse (f .read (), filename = filename )
68+
69+ names : Set [str ] = set ()
70+ for node in tree .body :
71+ if isinstance (node , ast .Import ):
72+ for alias in node .names :
73+ names .add (alias .asname or alias .name .split ("." )[0 ])
74+ elif isinstance (node , ast .ImportFrom ):
75+ for alias in node .names :
76+ if alias .name == "*" :
77+ # Wildcard imports can't be resolved statically; users who
78+ # hit this case can alias names with leading underscores.
79+ continue
80+ names .add (alias .asname or alias .name )
81+ return names
82+
83+
5884def _get_group_data (dirname_or_filename : str ):
5985 group_data = {}
6086
@@ -76,7 +102,14 @@ def _get_group_data(dirname_or_filename: str):
76102
77103 # Read the files locals into a dict
78104 attrs = exec_file (file , return_locals = True )
79- keys = attrs .get ("__all__" , attrs .keys ())
105+ # If __all__ is explicitly set the user controls exports directly;
106+ # otherwise drop names introduced by import statements so modules,
107+ # classes, and other unserializable objects don't leak into the
108+ # group data (issue #1297).
109+ if "__all__" in attrs :
110+ keys = attrs ["__all__" ]
111+ else :
112+ keys = [key for key in attrs .keys () if key not in _get_imported_names (file )]
80113
81114 group_data [group_name ] = {
82115 key : value
0 commit comments