PY-86308: Fix profiler module execution with -m flag

Problem:
- Profiling with -m flag failed to find modules in Python 3.12+
- pkgutil.get_loader() was deprecated in Python 3.3 and removed in Python 3.12
- Module arguments (e.g., -e, --mode) were incorrectly parsed as profiler arguments

Solution:
1. Updated get_fullname() in prof_util.py:
   - Use importlib.util.find_spec() for Python 3.x (modern API)
   - Keep pkgutil.get_loader() only for Python 2.7 (legacy support)

2. Fixed argument parsing in run_profiler.py:
   - When -m is specified, all remaining args are passed to the target module
   - Avoid double-parsing that caused "unrecognized arguments" errors

Space-RevId: 86889a34e12bd9cdc510eb5fce739ce96e870789

GitOrigin-RevId: f9b609ff9a36ad6ec02476507892e2b263322930
This commit is contained in:
Timur Malanin
2026-01-22 17:45:14 +00:00
committed by intellij-monorepo-bot
parent 35f5419082
commit 9262964625
2 changed files with 30 additions and 14 deletions
+19 -7
View File
@@ -45,15 +45,27 @@ def save_main_module(file, module_name):
def get_fullname(mod_name):
# Use pkgutil ONLY for Python 2.7 (legacy support)
if sys.version_info[:2] == (2, 7):
try:
loader = pkgutil.get_loader(mod_name)
except (ImportError, AttributeError, TypeError, ValueError):
return None
if loader is not None:
for attr in ("get_filename", "_get_filename"):
meth = getattr(loader, attr, None)
if meth is not None:
return meth(mod_name)
return None
# Use modern importlib API for all other Python versions (3.x)
try:
loader = pkgutil.get_loader(mod_name)
except:
import importlib.util
spec = importlib.util.find_spec(mod_name)
if spec is not None and spec.origin is not None:
return spec.origin
except (ImportError, AttributeError, TypeError, ValueError, ModuleNotFoundError):
return None
if loader is not None:
for attr in ("get_filename", "_get_filename"):
meth = getattr(loader, attr, None)
if meth is not None:
return meth(mod_name)
return None
+11 -7
View File
@@ -1,15 +1,16 @@
import argparse
import os
import sys
import time
import traceback
import argparse
from socket import AF_INET
from socket import SOCK_STREAM
from socket import socket
from _prof_imports import ProfilerResponse
from prof_io import ProfWriter, ProfReader
from prof_util import generate_snapshot_filepath, stats_to_response, get_snapshot_basepath, save_main_module, execfile, get_fullname
from prof_util import generate_snapshot_filepath, stats_to_response, \
get_snapshot_basepath, save_main_module, execfile, get_fullname
base_snapshot_path = os.getenv('PYCHARM_SNAPSHOT_PATH')
remote_run = bool(os.getenv('PYCHARM_REMOTE_RUN', ''))
@@ -170,17 +171,20 @@ def parse_arguments():
args, remaining_args = parser.parse_known_args()
if args.module:
# When -m is specified, all remaining args go to the module
args.file = None
return args, remaining_args
# When -m is not specified, parse file and its arguments
remaining_parser = argparse.ArgumentParser(add_help=False)
remaining_parser.add_argument('file', nargs='?', help='Python file to profile')
remaining_parser.add_argument('args', nargs=argparse.REMAINDER, help='Arguments to pass to the target')
remaining_namespace = remaining_parser.parse_args(remaining_args)
if not args.module and remaining_args:
args.file = remaining_namespace.file
remaining_args = remaining_namespace.args
else:
args.file = None
args.file = remaining_namespace.file
remaining_args = remaining_namespace.args
return args, remaining_args