langchain-prompty[patch]: Add ruff bandit rules to linter (#31814)

- Add ruff bandit rules
- Address some s101 assertion warnings
- Address s506 by using `yaml.safe_load()`
This commit is contained in:
Mason Daugherty 2025-07-01 14:32:02 -04:00 committed by GitHub
parent 3190c4132f
commit b03e326231
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 23 additions and 14 deletions

View File

@ -55,7 +55,7 @@ class TemplateSettings(BaseModel):
class Prompty(BaseModel): class Prompty(BaseModel):
"""Base Prompty model.""" """Base Prompty model."""
# metadata # Metadata
name: str = Field(default="") name: str = Field(default="")
description: str = Field(default="") description: str = Field(default="")
authors: list[str] = Field(default=[]) authors: list[str] = Field(default=[])
@ -63,17 +63,18 @@ class Prompty(BaseModel):
version: str = Field(default="") version: str = Field(default="")
base: str = Field(default="") base: str = Field(default="")
basePrompty: Optional[Prompty] = Field(default=None) basePrompty: Optional[Prompty] = Field(default=None)
# model
# Model
model: ModelSettings = Field(default_factory=ModelSettings) model: ModelSettings = Field(default_factory=ModelSettings)
# sample # Sample
sample: dict = Field(default={}) sample: dict = Field(default={})
# input / output # Input / output
inputs: dict[str, PropertySettings] = Field(default={}) inputs: dict[str, PropertySettings] = Field(default={})
outputs: dict[str, PropertySettings] = Field(default={}) outputs: dict[str, PropertySettings] = Field(default={})
# template # Template
template: TemplateSettings template: TemplateSettings
file: FilePath = Field(default="") # type: ignore[assignment] file: FilePath = Field(default="") # type: ignore[assignment]
@ -96,14 +97,14 @@ class Prompty(BaseModel):
else self.file else self.file
) )
elif k == "basePrompty": elif k == "basePrompty":
# no need to serialize basePrompty # No need to serialize basePrompty
continue continue
else: else:
d[k] = v d[k] = v
return d return d
# generate json representation of the prompty # Generate json representation of the prompty
def to_safe_json(self) -> str: def to_safe_json(self) -> str:
d = self.to_safe_dict() d = self.to_safe_dict()
return json.dumps(d) return json.dumps(d)
@ -307,9 +308,9 @@ class Frontmatter:
"""Returns dict with separated frontmatter from string. """Returns dict with separated frontmatter from string.
Returned dict keys: Returned dict keys:
attributes -- extracted YAML attributes in dict form. - attributes: extracted YAML attributes in dict form.
body -- string contents below the YAML separators - body: string contents below the YAML separators
frontmatter -- string representation of YAML - frontmatter: string representation of YAML
""" """
fmatter = "" fmatter = ""
body = "" body = ""
@ -319,7 +320,7 @@ class Frontmatter:
fmatter = result.group(1) fmatter = result.group(1)
body = result.group(2) body = result.group(2)
return { return {
"attributes": yaml.load(fmatter, Loader=yaml.FullLoader), "attributes": yaml.safe_load(fmatter),
"body": body, "body": body,
"frontmatter": fmatter, "frontmatter": fmatter,
} }

View File

@ -102,7 +102,8 @@ class PromptyChatParser(Invoker):
return content return content
def invoke(self, data: BaseModel) -> BaseModel: def invoke(self, data: BaseModel) -> BaseModel:
assert isinstance(data, SimpleModel) if not isinstance(data, SimpleModel):
raise ValueError("data must be an instance of SimpleModel")
messages = [] messages = []
separator = r"(?i)^\s*#?\s*(" + "|".join(self.roles) + r")\s*:\s*\n" separator = r"(?i)^\s*#?\s*(" + "|".join(self.roles) + r")\s*:\s*\n"

View File

@ -11,6 +11,7 @@ class MustacheRenderer(Invoker):
self.prompty = prompty self.prompty = prompty
def invoke(self, data: BaseModel) -> BaseModel: def invoke(self, data: BaseModel) -> BaseModel:
assert isinstance(data, SimpleModel) if not isinstance(data, SimpleModel):
raise ValueError("Expected data to be an instance of SimpleModel")
generated = mustache.render(self.prompty.content, data.item) generated = mustache.render(self.prompty.content, data.item)
return SimpleModel[str](item=generated) return SimpleModel[str](item=generated)

View File

@ -49,7 +49,7 @@ langchain = { path = "../../langchain", editable = true }
target-version = "py39" target-version = "py39"
[tool.ruff.lint] [tool.ruff.lint]
select = ["E", "F", "I", "T201", "UP"] select = ["E", "F", "I", "T201", "UP", "S"]
ignore = [ "UP007", ] ignore = [ "UP007", ]
[tool.mypy] [tool.mypy]
@ -65,3 +65,9 @@ markers = [
"compile: mark placeholder test used to compile integration tests without running them", "compile: mark placeholder test used to compile integration tests without running them",
] ]
asyncio_mode = "auto" asyncio_mode = "auto"
[tool.ruff.lint.extend-per-file-ignores]
"tests/**/*.py" = [
"S101", # Tests need assertions
"S311", # Standard pseudo-random generators are not suitable for cryptographic purposes
]