MindsDB 25.7.2.0__py3-none-any.whl → 25.7.4.0__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.

Potentially problematic release.


This version of MindsDB might be problematic. Click here for more details.

Files changed (69) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +1 -1
  3. mindsdb/api/a2a/common/server/server.py +16 -6
  4. mindsdb/api/executor/command_executor.py +213 -137
  5. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +5 -1
  6. mindsdb/api/executor/datahub/datanodes/project_datanode.py +14 -3
  7. mindsdb/api/executor/planner/plan_join.py +3 -0
  8. mindsdb/api/executor/planner/plan_join_ts.py +117 -100
  9. mindsdb/api/executor/planner/query_planner.py +1 -0
  10. mindsdb/api/executor/sql_query/steps/apply_predictor_step.py +54 -85
  11. mindsdb/api/http/initialize.py +16 -43
  12. mindsdb/api/http/namespaces/agents.py +24 -21
  13. mindsdb/api/http/namespaces/chatbots.py +83 -120
  14. mindsdb/api/http/namespaces/file.py +1 -1
  15. mindsdb/api/http/namespaces/jobs.py +38 -60
  16. mindsdb/api/http/namespaces/tree.py +69 -61
  17. mindsdb/api/mcp/start.py +2 -0
  18. mindsdb/api/mysql/mysql_proxy/utilities/dump.py +3 -2
  19. mindsdb/integrations/handlers/autogluon_handler/requirements.txt +1 -1
  20. mindsdb/integrations/handlers/autosklearn_handler/requirements.txt +1 -1
  21. mindsdb/integrations/handlers/bigquery_handler/bigquery_handler.py +25 -5
  22. mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +3 -3
  23. mindsdb/integrations/handlers/flaml_handler/requirements.txt +1 -1
  24. mindsdb/integrations/handlers/google_calendar_handler/google_calendar_tables.py +82 -73
  25. mindsdb/integrations/handlers/hubspot_handler/requirements.txt +1 -1
  26. mindsdb/integrations/handlers/langchain_handler/langchain_handler.py +83 -76
  27. mindsdb/integrations/handlers/lightwood_handler/requirements.txt +4 -4
  28. mindsdb/integrations/handlers/litellm_handler/litellm_handler.py +16 -3
  29. mindsdb/integrations/handlers/litellm_handler/settings.py +2 -1
  30. mindsdb/integrations/handlers/llama_index_handler/requirements.txt +1 -1
  31. mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +106 -90
  32. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +41 -39
  33. mindsdb/integrations/handlers/s3_handler/s3_handler.py +72 -70
  34. mindsdb/integrations/handlers/salesforce_handler/constants.py +208 -0
  35. mindsdb/integrations/handlers/salesforce_handler/salesforce_handler.py +142 -81
  36. mindsdb/integrations/handlers/salesforce_handler/salesforce_tables.py +12 -4
  37. mindsdb/integrations/handlers/slack_handler/slack_tables.py +141 -161
  38. mindsdb/integrations/handlers/tpot_handler/requirements.txt +1 -1
  39. mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +32 -17
  40. mindsdb/integrations/handlers/web_handler/web_handler.py +19 -22
  41. mindsdb/integrations/handlers/youtube_handler/youtube_tables.py +183 -55
  42. mindsdb/integrations/libs/vectordatabase_handler.py +10 -1
  43. mindsdb/integrations/utilities/handler_utils.py +32 -12
  44. mindsdb/interfaces/agents/agents_controller.py +169 -110
  45. mindsdb/interfaces/agents/langchain_agent.py +10 -3
  46. mindsdb/interfaces/data_catalog/data_catalog_loader.py +22 -8
  47. mindsdb/interfaces/database/database.py +38 -13
  48. mindsdb/interfaces/database/integrations.py +20 -5
  49. mindsdb/interfaces/database/projects.py +63 -16
  50. mindsdb/interfaces/database/views.py +86 -60
  51. mindsdb/interfaces/jobs/jobs_controller.py +103 -110
  52. mindsdb/interfaces/knowledge_base/controller.py +33 -5
  53. mindsdb/interfaces/knowledge_base/evaluate.py +53 -9
  54. mindsdb/interfaces/knowledge_base/executor.py +24 -0
  55. mindsdb/interfaces/knowledge_base/llm_client.py +3 -3
  56. mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +21 -13
  57. mindsdb/interfaces/query_context/context_controller.py +100 -133
  58. mindsdb/interfaces/skills/skills_controller.py +18 -6
  59. mindsdb/interfaces/storage/db.py +40 -6
  60. mindsdb/interfaces/variables/variables_controller.py +8 -15
  61. mindsdb/utilities/config.py +3 -3
  62. mindsdb/utilities/functions.py +72 -60
  63. mindsdb/utilities/log.py +38 -6
  64. mindsdb/utilities/ps.py +7 -7
  65. {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/METADATA +262 -263
  66. {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/RECORD +69 -68
  67. {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/WHEEL +0 -0
  68. {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/licenses/LICENSE +0 -0
  69. {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/top_level.txt +0 -0
@@ -23,13 +23,16 @@ class SkillsController:
23
23
  project_controller = ProjectController()
24
24
  self.project_controller = project_controller
25
25
 
26
- def get_skill(self, skill_name: str, project_name: str = default_project) -> Optional[db.Skills]:
26
+ def get_skill(
27
+ self, skill_name: str, project_name: str = default_project, strict_case: bool = False
28
+ ) -> Optional[db.Skills]:
27
29
  """
28
30
  Gets a skill by name. Skills are expected to have unique names.
29
31
 
30
32
  Parameters:
31
33
  skill_name (str): The name of the skill
32
34
  project_name (str): The name of the containing project
35
+ strict_case (bool): If True, the skill name is case-sensitive. Defaults to False.
33
36
 
34
37
  Returns:
35
38
  skill (Optional[db.Skills]): The database skill object
@@ -39,11 +42,16 @@ class SkillsController:
39
42
  """
40
43
 
41
44
  project = self.project_controller.get(name=project_name)
42
- return db.Skills.query.filter(
43
- func.lower(db.Skills.name) == func.lower(skill_name),
45
+ query = db.Skills.query.filter(
44
46
  db.Skills.project_id == project.id,
45
47
  db.Skills.deleted_at == null(),
46
- ).first()
48
+ )
49
+ if strict_case:
50
+ query = query.filter(db.Skills.name == skill_name)
51
+ else:
52
+ query = query.filter(func.lower(db.Skills.name) == func.lower(skill_name))
53
+
54
+ return query.first()
47
55
 
48
56
  def get_skills(self, project_name: Optional[str]) -> List[dict]:
49
57
  """
@@ -92,6 +100,9 @@ class SkillsController:
92
100
  project_name = default_project
93
101
  project = self.project_controller.get(name=project_name)
94
102
 
103
+ if not name.islower():
104
+ raise ValueError(f"The name must be in lower case: {name}")
105
+
95
106
  skill = self.get_skill(name, project_name)
96
107
 
97
108
  if skill is not None:
@@ -158,19 +169,20 @@ class SkillsController:
158
169
 
159
170
  return existing_skill
160
171
 
161
- def delete_skill(self, skill_name: str, project_name: str = default_project):
172
+ def delete_skill(self, skill_name: str, project_name: str = default_project, strict_case: bool = False):
162
173
  """
163
174
  Deletes a skill by name.
164
175
 
165
176
  Parameters:
166
177
  skill_name (str): The name of the skill to delete
167
178
  project_name (str): The name of the containing project
179
+ strict_case (bool): If true, then skill_name is case sensitive
168
180
 
169
181
  Raises:
170
182
  ValueError: If `project_name` does not exist or skill doesn't exist
171
183
  """
172
184
 
173
- skill = self.get_skill(skill_name, project_name)
185
+ skill = self.get_skill(skill_name, project_name, strict_case)
174
186
  if skill is None:
175
187
  raise ValueError(f"Skill with name doesn't exist: {skill_name}")
176
188
  if isinstance(skill.params, dict) and skill.params.get("is_demo") is True:
@@ -448,19 +448,53 @@ class Agents(Base):
448
448
  deleted_at = Column(DateTime)
449
449
 
450
450
  def as_dict(self) -> Dict:
451
- return {
451
+ skills = []
452
+ skills_extra_parameters = {}
453
+ for rel in self.skills_relationships:
454
+ skill = rel.skill
455
+ # Skip auto-generated SQL skills
456
+ if skill.params.get("description", "").startswith("Auto-generated SQL skill for agent"):
457
+ continue
458
+ skills.append(skill.as_dict())
459
+ skills_extra_parameters[skill.name] = rel.parameters or {}
460
+
461
+ params = self.params.copy()
462
+
463
+ agent_dict = {
452
464
  "id": self.id,
453
465
  "name": self.name,
454
466
  "project_id": self.project_id,
455
- "model_name": self.model_name,
456
- "skills": [rel.skill.as_dict() for rel in self.skills_relationships],
457
- "skills_extra_parameters": {rel.skill.name: (rel.parameters or {}) for rel in self.skills_relationships},
458
- "provider": self.provider,
459
- "params": self.params,
460
467
  "updated_at": self.updated_at,
461
468
  "created_at": self.created_at,
462
469
  }
463
470
 
471
+ if self.model_name:
472
+ agent_dict["model_name"] = self.model_name
473
+
474
+ if self.provider:
475
+ agent_dict["provider"] = self.provider
476
+
477
+ # Since skills were depreciated, they are only used with Minds
478
+ # Minds expects the parameters to be provided as is without breaking them down
479
+ if skills:
480
+ agent_dict["skills"] = skills
481
+ agent_dict["skills_extra_parameters"] = skills_extra_parameters
482
+ agent_dict["params"] = params
483
+ else:
484
+ data = params.pop("data", {})
485
+ model = params.pop("model", {})
486
+ prompt_template = params.pop("prompt_template", None)
487
+ if data:
488
+ agent_dict["data"] = data
489
+ if model:
490
+ agent_dict["model"] = model
491
+ if prompt_template:
492
+ agent_dict["prompt_template"] = prompt_template
493
+ if params:
494
+ agent_dict["params"] = params
495
+
496
+ return agent_dict
497
+
464
498
 
465
499
  class KnowledgeBase(Base):
466
500
  __tablename__ = "knowledge_base"
@@ -15,13 +15,9 @@ ENV_VAR_PREFIX = "MDB_"
15
15
 
16
16
 
17
17
  class VariablesController:
18
-
19
18
  def __init__(self) -> None:
20
- self._storage = get_json_storage(
21
- resource_id=0,
22
- resource_group=RESOURCE_GROUP.SYSTEM
23
- )
24
- self._store_key = 'variables'
19
+ self._storage = get_json_storage(resource_id=0, resource_group=RESOURCE_GROUP.SYSTEM)
20
+ self._store_key = "variables"
25
21
  self._data = None
26
22
 
27
23
  def _get_data(self) -> dict:
@@ -54,7 +50,7 @@ class VariablesController:
54
50
  return os.environ[var_name]
55
51
 
56
52
  def _get_function(self, name: str) -> Callable:
57
- if name == 'from_env':
53
+ if name == "from_env":
58
54
  return self._from_env
59
55
  raise ValueError(f"Function {name} is not found")
60
56
 
@@ -81,16 +77,13 @@ class VariablesController:
81
77
 
82
78
  if isinstance(var, Variable):
83
79
  return self.get_value(var.value.lower())
80
+ if isinstance(var, Function):
81
+ fnc = self._get_function(var.op)
82
+ return fnc(*var.args)
84
83
  elif isinstance(var, dict):
85
- return {
86
- key: self.fill_parameters(value)
87
- for key, value in var.items()
88
- }
84
+ return {key: self.fill_parameters(value) for key, value in var.items()}
89
85
  elif isinstance(var, list):
90
- return [
91
- self.fill_parameters(value)
92
- for value in var
93
- ]
86
+ return [self.fill_parameters(value) for value in var]
94
87
  return var
95
88
 
96
89
 
@@ -318,7 +318,7 @@ class Config:
318
318
  self._env_config["logging"]["handlers"]["console"]["level"] = os.environ["MINDSDB_LOG_LEVEL"]
319
319
  self._env_config["logging"]["handlers"]["console"]["enabled"] = True
320
320
  if os.environ.get("MINDSDB_CONSOLE_LOG_LEVEL", "") != "":
321
- self._env_config["logging"]["handlers"]["console"]["level"] = os.environ["MINDSDB_LOG_LEVEL"]
321
+ self._env_config["logging"]["handlers"]["console"]["level"] = os.environ["MINDSDB_CONSOLE_LOG_LEVEL"]
322
322
  self._env_config["logging"]["handlers"]["console"]["enabled"] = True
323
323
  if os.environ.get("MINDSDB_FILE_LOG_LEVEL", "") != "":
324
324
  self._env_config["logging"]["handlers"]["file"]["level"] = os.environ["MINDSDB_FILE_LOG_LEVEL"]
@@ -459,8 +459,8 @@ class Config:
459
459
  """Merge multiple configs to one."""
460
460
  new_config = deepcopy(self._default_config)
461
461
  _merge_configs(new_config, self._user_config)
462
- _merge_configs(new_config, self._auto_config)
463
- _merge_configs(new_config, self._env_config)
462
+ _merge_configs(new_config, self._auto_config or {})
463
+ _merge_configs(new_config, self._env_config or {})
464
464
 
465
465
  # Apply command-line arguments for A2A
466
466
  a2a_config = {}
@@ -35,20 +35,19 @@ def get_handler_install_message(handler_name):
35
35
 
36
36
 
37
37
  def cast_row_types(row, field_types):
38
- '''
39
- '''
38
+ """ """
40
39
  keys = [x for x in row.keys() if x in field_types]
41
40
  for key in keys:
42
41
  t = field_types[key]
43
- if t == 'Timestamp' and isinstance(row[key], (int, float)):
44
- timestamp = datetime.datetime.utcfromtimestamp(row[key])
45
- row[key] = timestamp.strftime('%Y-%m-%d %H:%M:%S')
46
- elif t == 'Date' and isinstance(row[key], (int, float)):
47
- timestamp = datetime.datetime.utcfromtimestamp(row[key])
48
- row[key] = timestamp.strftime('%Y-%m-%d')
49
- elif t == 'Int' and isinstance(row[key], (int, float, str)):
42
+ if t == "Timestamp" and isinstance(row[key], (int, float)):
43
+ timestamp = datetime.datetime.fromtimestamp(row[key], datetime.timezone.utc)
44
+ row[key] = timestamp.strftime("%Y-%m-%d %H:%M:%S")
45
+ elif t == "Date" and isinstance(row[key], (int, float)):
46
+ timestamp = datetime.datetime.fromtimestamp(row[key], datetime.timezone.utc)
47
+ row[key] = timestamp.strftime("%Y-%m-%d")
48
+ elif t == "Int" and isinstance(row[key], (int, float, str)):
50
49
  try:
51
- logger.debug(f'cast {row[key]} to {int(row[key])}')
50
+ logger.debug(f"cast {row[key]} to {int(row[key])}")
52
51
  row[key] = int(row[key])
53
52
  except Exception:
54
53
  pass
@@ -67,13 +66,16 @@ def mark_process(name: str, custom_mark: str = None) -> Callable:
67
66
  return func(*args, **kwargs)
68
67
  finally:
69
68
  delete_process_mark(name, mark)
69
+
70
70
  return wrapper
71
+
71
72
  return mark_process_wrapper
72
73
 
73
74
 
74
75
  def init_lexer_parsers():
75
76
  from mindsdb_sql_parser.lexer import MindsDBLexer
76
77
  from mindsdb_sql_parser.parser import MindsDBParser
78
+
77
79
  return MindsDBLexer(), MindsDBParser()
78
80
 
79
81
 
@@ -86,62 +88,72 @@ def resolve_table_identifier(identifier: Identifier, default_database: str = Non
86
88
  elif parts_count == 2:
87
89
  return (parts[0], parts[1])
88
90
  else:
89
- raise Exception(f'Table identifier must contain max 2 parts: {parts}')
91
+ raise Exception(f"Table identifier must contain max 2 parts: {parts}")
90
92
 
91
93
 
92
94
  def resolve_model_identifier(identifier: Identifier) -> tuple:
93
- """ split model name to parts
94
-
95
- Identifier may be:
96
-
97
- Examples:
98
- >>> resolve_model_identifier(['a', 'b'])
99
- ('a', 'b', None)
100
-
101
- >>> resolve_model_identifier(['a', '1'])
102
- (None, 'a', 1)
103
-
104
- >>> resolve_model_identifier(['a'])
105
- (None, 'a', None)
106
-
107
- >>> resolve_model_identifier(['a', 'b', 'c'])
108
- (None, None, None) # not found
109
-
110
- Args:
111
- name (Identifier): Identifier parts
112
-
113
- Returns:
114
- tuple: (database_name, model_name, model_version)
115
95
  """
116
- parts = identifier.parts
117
- database_name = None
96
+ Splits a model identifier into its database, model name, and version components.
97
+
98
+ The identifier may contain one, two, or three parts.
99
+ The function supports both quoted and unquoted identifiers, and normalizes names to lowercase if unquoted.
100
+
101
+ Examples:
102
+ >>> resolve_model_identifier(Identifier(parts=['a', 'b']))
103
+ ('a', 'b', None)
104
+ >>> resolve_model_identifier(Identifier(parts=['a', '1']))
105
+ (None, 'a', 1)
106
+ >>> resolve_model_identifier(Identifier(parts=['a']))
107
+ (None, 'a', None)
108
+ >>> resolve_model_identifier(Identifier(parts=['a', 'b', 'c']))
109
+ (None, None, None) # not found
110
+
111
+ Args:
112
+ identifier (Identifier): The identifier object containing parts and is_quoted attributes.
113
+
114
+ Returns:
115
+ tuple: (database_name, model_name, model_version)
116
+ - database_name (str or None): The name of the database/project, or None if not specified.
117
+ - model_name (str or None): The name of the model, or None if not found.
118
+ - model_version (int or None): The model version as an integer, or None if not specified.
119
+ """
118
120
  model_name = None
119
- model_version = None
121
+ db_name = None
122
+ version = None
123
+ model_name_quoted = None
124
+ db_name_quoted = None
125
+
126
+ match identifier.parts, identifier.is_quoted:
127
+ case [model_name], [model_name_quoted]:
128
+ ...
129
+ case [model_name, str(version)], [model_name_quoted, _] if version.isdigit():
130
+ ...
131
+ case [model_name, int(version)], [model_name_quoted, _]:
132
+ ...
133
+ case [db_name, model_name], [db_name_quoted, model_name_quoted]:
134
+ ...
135
+ case [db_name, model_name, str(version)], [db_name_quoted, model_name_quoted, _] if version.isdigit():
136
+ ...
137
+ case [db_name, model_name, int(version)], [db_name_quoted, model_name_quoted, _]:
138
+ ...
139
+ case [db_name, model_name, str(version)], [db_name_quoted, model_name_quoted, _]:
140
+ # for back compatibility. May be delete?
141
+ return (None, None, None)
142
+ case _:
143
+ ... # may be raise ValueError?
144
+
145
+ if model_name_quoted is False:
146
+ model_name = model_name.lower()
147
+
148
+ if db_name_quoted is False:
149
+ db_name = db_name.lower()
150
+
151
+ if isinstance(version, int) or isinstance(version, str) and version.isdigit():
152
+ version = int(version)
153
+ else:
154
+ version = None
120
155
 
121
- parts_count = len(parts)
122
- if parts_count == 1:
123
- database_name = None
124
- model_name = parts[0]
125
- model_version = None
126
- elif parts_count == 2:
127
- if parts[-1].isdigit():
128
- database_name = None
129
- model_name = parts[0]
130
- model_version = int(parts[-1])
131
- else:
132
- database_name = parts[0]
133
- model_name = parts[1]
134
- model_version = None
135
- elif parts_count == 3:
136
- database_name = parts[0]
137
- model_name = parts[1]
138
- if parts[2].isdigit():
139
- model_version = int(parts[2])
140
- else:
141
- # not found
142
- return None, None, None
143
-
144
- return database_name, model_name, model_version
156
+ return db_name, model_name, version
145
157
 
146
158
 
147
159
  def encrypt(string: bytes, key: str) -> bytes:
mindsdb/utilities/log.py CHANGED
@@ -43,6 +43,13 @@ class ColorFormatter(logging.Formatter):
43
43
  return log_fmt.format(record)
44
44
 
45
45
 
46
+ FORMATTERS = {
47
+ "default": {"()": ColorFormatter},
48
+ "json": {"()": JsonFormatter},
49
+ "file": {"format": "%(asctime)s %(processName)15s %(levelname)-8s %(name)s: %(message)s"},
50
+ }
51
+
52
+
46
53
  def get_console_handler_config_level() -> int:
47
54
  console_handler_config = app_config["logging"]["handlers"]["console"]
48
55
  return getattr(logging, console_handler_config["level"])
@@ -60,7 +67,7 @@ def get_mindsdb_log_level() -> int:
60
67
  return min(console_handler_config_level, file_handler_config_level)
61
68
 
62
69
 
63
- def configure_logging(process_name: str = None):
70
+ def get_handlers_config(process_name: str) -> dict:
64
71
  handlers_config = {}
65
72
  console_handler_config = app_config["logging"]["handlers"]["console"]
66
73
  console_handler_config_level = getattr(logging, console_handler_config["level"])
@@ -89,16 +96,41 @@ def configure_logging(process_name: str = None):
89
96
  "maxBytes": file_handler_config["maxBytes"], # 0.5 Mb
90
97
  "backupCount": file_handler_config["backupCount"],
91
98
  }
99
+ return handlers_config
100
+
101
+
102
+ def get_uvicorn_logging_config(process_name: str) -> dict:
103
+ """Generate a logging configuration dictionary for Uvicorn using MindsDB's logging settings.
104
+
105
+ Args:
106
+ process_name (str): The name of the process to include in log file names and handlers.
107
+
108
+ Returns:
109
+ dict: A dictionary suitable for use with logging.config.dictConfig, configured for Uvicorn logging.
110
+ """
111
+ handlers_config = get_handlers_config(process_name)
112
+ mindsdb_log_level = get_mindsdb_log_level()
113
+ return {
114
+ "version": 1,
115
+ "formatters": FORMATTERS,
116
+ "handlers": handlers_config,
117
+ "loggers": {
118
+ "uvicorn": {
119
+ "handlers": list(handlers_config.keys()),
120
+ "level": mindsdb_log_level,
121
+ "propagate": False,
122
+ }
123
+ },
124
+ }
125
+
92
126
 
127
+ def configure_logging(process_name: str = None):
128
+ handlers_config = get_handlers_config(process_name)
93
129
  mindsdb_log_level = get_mindsdb_log_level()
94
130
 
95
131
  logging_config = dict(
96
132
  version=1,
97
- formatters={
98
- "default": {"()": ColorFormatter},
99
- "json": {"()": JsonFormatter},
100
- "file": {"format": "%(asctime)s %(processName)15s %(levelname)-8s %(name)s: %(message)s"},
101
- },
133
+ formatters=FORMATTERS,
102
134
  handlers=handlers_config,
103
135
  loggers={
104
136
  "": { # root logger
mindsdb/utilities/ps.py CHANGED
@@ -11,23 +11,23 @@ def get_child_pids(pid):
11
11
 
12
12
  def net_connections():
13
13
  """Cross-platform psutil.net_connections like interface"""
14
- if sys.platform.lower().startswith('linux'):
14
+ if sys.platform.lower().startswith("linux"):
15
15
  return psutil.net_connections()
16
16
 
17
17
  all_connections = []
18
18
  Pconn = None
19
- for p in psutil.process_iter(['pid']):
19
+ for p in psutil.process_iter(["pid"]):
20
20
  try:
21
21
  process = psutil.Process(p.pid)
22
- connections = process.connections()
22
+ connections = process.net_connections()
23
23
  if connections:
24
24
  for conn in connections:
25
25
  # Adding pid to the returned instance
26
26
  # for consistency with psutil.net_connections()
27
27
  if Pconn is None:
28
28
  fields = list(conn._fields)
29
- fields.append('pid')
30
- _conn = namedtuple('Pconn', fields)
29
+ fields.append("pid")
30
+ _conn = namedtuple("Pconn", fields)
31
31
  for attr in conn._fields:
32
32
  setattr(_conn, attr, getattr(conn, attr))
33
33
  _conn.pid = p.pid
@@ -43,7 +43,7 @@ def is_port_in_use(port_num):
43
43
  parent_process = psutil.Process()
44
44
  child_pids = [x.pid for x in parent_process.children(recursive=True)]
45
45
  conns = net_connections()
46
- portsinuse = [x.laddr[1] for x in conns if x.pid in child_pids and x.status == 'LISTEN']
46
+ portsinuse = [x.laddr[1] for x in conns if x.pid in child_pids and x.status == "LISTEN"]
47
47
  portsinuse.sort()
48
48
  return int(port_num) in portsinuse
49
49
 
@@ -66,7 +66,7 @@ def wait_port(port_num, timeout):
66
66
  def get_listen_ports(pid):
67
67
  try:
68
68
  p = psutil.Process(pid)
69
- cons = p.connections()
69
+ cons = p.net_connections()
70
70
  cons = [x.laddr.port for x in cons]
71
71
  except Exception:
72
72
  return []