Skip to content

Commit 5f87cef

Browse files
committed
fix: poetry error on source and parsing pyproject.toml
Now we auto-add the Safety source and also we fixed the issues parsing Poetry projects.
1 parent d9350f2 commit 5f87cef

File tree

5 files changed

+73
-31
lines changed

5 files changed

+73
-31
lines changed

safety/tool/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ def can_handle(self, args: List[str]) -> bool:
344344
"""
345345
Check if this parser can handle the given arguments
346346
"""
347-
if not args or len(args) < 2:
347+
if not args or len(args) < 1:
348348
return False
349349

350350
return args[0].lower() in self.intention_mapping

safety/tool/poetry/command.py

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from pathlib import Path
22
import sys
33
from typing import TYPE_CHECKING, List, Optional, Tuple
4-
4+
import logging
55
import typer
66

7-
from .constants import MSG_MISSING_SAFETY_SOURCE
7+
from safety.tool.utils import PoetryPyprojectConfigurator
8+
9+
from .constants import MSG_SAFETY_SOURCE_ADDED, MSG_SAFETY_SOURCE_NOT_ADDED
810
from .parser import PoetryParser
911

1012
from ..base import BaseCommand, ToolIntentionType
@@ -24,6 +26,8 @@
2426
else:
2527
import tomli as tomllib
2628

29+
logger = logging.getLogger(__name__)
30+
2731

2832
class PoetryCommand(BaseCommand):
2933
"""
@@ -74,23 +78,7 @@ def get_package_list_command(self) -> List[str]:
7478
"""
7579
return ["poetry", "run", "pip", "list", "--format=json"]
7680

77-
@classmethod
78-
def from_args(cls, args: List[str], **kwargs):
79-
parser = PoetryParser()
80-
81-
if intention := parser.parse(args):
82-
if intention.intention_type is ToolIntentionType.ADD_PACKAGE:
83-
return PoetryAddCommand(args, intention=intention, **kwargs)
84-
85-
return PoetryGenericCommand(args, **kwargs)
86-
87-
88-
class PoetryGenericCommand(PoetryCommand):
89-
pass
90-
91-
92-
class PoetryAddCommand(PoetryCommand):
93-
def has_safety_source_in_pyproject(self) -> bool:
81+
def _has_safety_source_in_pyproject(self) -> bool:
9482
"""
9583
Check if 'safety' source exists in pyproject.toml
9684
"""
@@ -113,6 +101,55 @@ def has_safety_source_in_pyproject(self) -> bool:
113101
except (FileNotFoundError, KeyError, tomllib.TOMLDecodeError):
114102
return False
115103

104+
def before(self, ctx: typer.Context):
105+
super().before(ctx)
106+
107+
if self._intention and self._intention.intention_type in [
108+
ToolIntentionType.SYNC_PACKAGES,
109+
ToolIntentionType.ADD_PACKAGE,
110+
]:
111+
if not self._has_safety_source_in_pyproject():
112+
org_slug = None
113+
try:
114+
data = ctx.obj.auth.client.initialize()
115+
org_slug = data.get("organization-data", {}).get("slug")
116+
except Exception:
117+
logger.exception(
118+
"Unable to pull the org slug from the initialize endpoint."
119+
)
120+
121+
try:
122+
configurator = PoetryPyprojectConfigurator()
123+
prj_slug = ctx.obj.project.id if ctx.obj.project else None
124+
if configurator.configure(
125+
Path("pyproject.toml"), org_slug, prj_slug
126+
):
127+
console.print(
128+
MSG_SAFETY_SOURCE_ADDED,
129+
)
130+
except Exception:
131+
logger.exception("Unable to configure the pyproject.toml file.")
132+
console.print(
133+
MSG_SAFETY_SOURCE_NOT_ADDED,
134+
)
135+
136+
@classmethod
137+
def from_args(cls, args: List[str], **kwargs):
138+
parser = PoetryParser()
139+
140+
if intention := parser.parse(args):
141+
kwargs["intention"] = intention
142+
if intention.intention_type is ToolIntentionType.ADD_PACKAGE:
143+
return PoetryAddCommand(args, **kwargs)
144+
145+
return PoetryGenericCommand(args, **kwargs)
146+
147+
148+
class PoetryGenericCommand(PoetryCommand):
149+
pass
150+
151+
152+
class PoetryAddCommand(PoetryCommand):
116153
def patch_source_option(
117154
self, args: List[str], new_source: str = "safety"
118155
) -> Tuple[Optional[str], List[str]]:
@@ -146,11 +183,5 @@ def patch_source_option(
146183
def before(self, ctx: typer.Context):
147184
super().before(ctx)
148185

149-
if not self.has_safety_source_in_pyproject():
150-
console.print(
151-
MSG_MISSING_SAFETY_SOURCE,
152-
)
153-
sys.exit(1)
154-
155186
_, modified_args = self.patch_source_option(self._args)
156187
self._args = modified_args

safety/tool/poetry/constants.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
MSG_MISSING_SAFETY_SOURCE = "\nError: Safety Firewall source is not configured in pyproject.toml. Continuing with installation would result in you not being protected from malicious or insecure packages. Run 'safety init' to fix this."
1+
MSG_SAFETY_SOURCE_NOT_ADDED = "\nError: Safety Firewall could not be added as a source in your pyproject.toml file. You will not be protected from malicious or insecure packages. Please run `safety init` to fix this."
2+
MSG_SAFETY_SOURCE_ADDED = (
3+
"\nSafety Firewall has been added as a source to protect this codebase"
4+
)

safety/tool/poetry/main.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,17 @@ def is_installed(cls) -> bool:
3838
def is_poetry_project_file(cls, file: Path) -> bool:
3939
try:
4040
cfg = tomllib.loads(file.read_text())
41-
return cfg.get("build-system", {}).get("requires") in [
42-
["poetry-core"],
43-
"poetry-core",
44-
]
41+
42+
# First check: tool.poetry section (most definitive)
43+
if "tool" in cfg and "poetry" in cfg.get("tool", {}):
44+
return True
45+
46+
# Extra check on build-system section
47+
build_backend = cfg.get("build-system", {}).get("build-backend", "")
48+
if build_backend and "poetry.core" in build_backend:
49+
return True
50+
51+
return False
4552
except (IOError, ValueError):
4653
return False
4754

safety/tool/poetry/parser.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def intention_mapping(self) -> Dict[str, ToolIntentionType]:
2020
"show": ToolIntentionType.LIST_PACKAGES,
2121
"init": ToolIntentionType.INIT_PROJECT,
2222
"build": ToolIntentionType.BUILD_PROJECT,
23+
"install": ToolIntentionType.SYNC_PACKAGES,
2324
}
2425

2526
def parse(self, args: List[str]) -> Optional[CommandToolIntention]:

0 commit comments

Comments
 (0)