pub struct PypiParser;Expand description
Parser for Python dependency manifests.
Supports pyproject.toml (PEP 621, PEP 735, Poetry, PEP 517/518) via
PypiParser::parse_content and requirements.txt/constraints.txt
(pip’s requirements file format) via
PypiParser::parse_requirements.
§Examples
use deps_pypi::parser::PypiParser;
use tower_lsp_server::ls_types::Uri;
let content = r#"
[project]
dependencies = ["requests>=2.28.0", "flask[async]>=3.0"]
"#;
let parser = PypiParser::new();
let uri = Uri::from_file_path("/test/pyproject.toml").unwrap();
let result = parser.parse_content(content, &uri).unwrap();
assert_eq!(result.dependencies.len(), 2);Implementations§
Source§impl PypiParser
impl PypiParser
Sourcepub fn parse_content(
&self,
content: &str,
uri: &Uri,
) -> Result<ParseResult, PypiError>
pub fn parse_content( &self, content: &str, uri: &Uri, ) -> Result<ParseResult, PypiError>
Parse pyproject.toml content and extract all dependencies.
Parses both PEP 621 and Poetry formats in a single pass.
§Errors
Returns an error if:
- TOML is malformed
- PEP 508 dependency specifications are invalid
§Examples
let parser = PypiParser::new();
let content = std::fs::read_to_string("pyproject.toml").unwrap();
let uri = Uri::from_file_path("/project/pyproject.toml").unwrap();
let result = parser.parse_content(&content, &uri).unwrap();Sourcepub fn parse_content_with_policy(
&self,
content: &str,
uri: &Uri,
policy: &RegistryAccessPolicy,
) -> Result<ParseResult, PypiError>
pub fn parse_content_with_policy( &self, content: &str, uri: &Uri, policy: &RegistryAccessPolicy, ) -> Result<ParseResult, PypiError>
Like Self::parse_content, but resolves [[tool.poetry.source]]/
[tool.uv.index]/[tool.uv.sources] index declarations (spec FR-002/003/005–007/013)
against policy rather than the default (public_only) — the production entry point
PypiEcosystem calls, threading through its own live RegistryAccessPolicy handle.
§Errors
Same as Self::parse_content.
Source§impl PypiParser
impl PypiParser
Sourcepub fn parse_requirements(
&self,
content: &str,
uri: &Uri,
require_strong_signal: bool,
) -> Result<ParseResult, PypiError>
pub fn parse_requirements( &self, content: &str, uri: &Uri, require_strong_signal: bool, ) -> Result<ParseResult, PypiError>
Parses a requirements.txt/constraints.txt file (pip’s
requirements file format) and extracts all dependencies.
Reuses the shared PEP 508 machinery
(PypiParser::parse_pep508_requirement) for every requirement line,
so hover, diagnostics, markers and extras render identically to
pyproject.toml. A line that fails to parse is logged and skipped
rather than failing the whole file — a requirements file is
free-form text under active editing, and a half-typed line must not
blank out hover for every other dependency.
Applies a content gate before returning: since .txt is routed here
by filename pattern rather than a fixed name, a prose file that
happens to match (product-requirements.txt) would otherwise send a
PyPI request and emit an “Unknown package” warning for every
single-word line. The gate keeps the parsed dependencies only if the
file shows a strong pip signal — a recognized option, or a
successfully parsed line whose dependency carries a version
requirement or a Git/URL source — or, when require_strong_signal is
false, more lines parsed than failed — real prose fails every line,
but a hand-written unpinned requests\nflask\nnumpy (or that file
mid-edit, with one partially typed line) survives. The signal is read
off the parsed dependency rather than scanned from raw text, so an
operator- or @-looking substring inside a prose sentence (an email
address, a comparison) cannot short-circuit the gate.
require_strong_signal drops that ratio-based arm entirely, requiring
the strong-signal check alone. Set this when the caller routed to this
parser via a weaker signal than a basename match — PyPI’s
requirements/*.txt Ecosystem::manifest_directory_patterns
fallback matches every .txt file under a directory literally named
requirements/, including requirements-engineering docs folders with
no relation to Python; without this, prose lines that happen to parse
as bare PEP 508 names ("Introduction", "Scope") can still clear the
ratio arm and trigger live PyPI lookups on what is not a manifest at
all (#452 S6).
§Errors
Never actually errs — the Result return type exists for symmetry
with PypiParser::parse_content.
§Examples
use deps_pypi::parser::PypiParser;
use tower_lsp_server::ls_types::Uri;
let parser = PypiParser::new();
let uri = Uri::from_file_path("/project/requirements.txt").unwrap();
let result = parser
.parse_requirements("requests==2.31.0\nflask>=3.0\n", &uri, false)
.unwrap();
assert_eq!(result.dependencies.len(), 2);Sourcepub fn parse_requirements_with_policy(
&self,
content: &str,
uri: &Uri,
require_strong_signal: bool,
policy: &RegistryAccessPolicy,
) -> Result<ParseResult, PypiError>
pub fn parse_requirements_with_policy( &self, content: &str, uri: &Uri, require_strong_signal: bool, policy: &RegistryAccessPolicy, ) -> Result<ParseResult, PypiError>
Like Self::parse_requirements, but resolves --index-url/--extra-index-url
declarations (spec FR-001–FR-006) against policy rather than the default
(public_only) — the production entry point PypiEcosystem calls, threading through
its own live RegistryAccessPolicy handle.
§Errors
Same as Self::parse_requirements — never actually errs.
Source§impl PypiParser
impl PypiParser
Sourcepub const fn new() -> PypiParser
pub const fn new() -> PypiParser
Create a new PyPI parser.
Trait Implementations§
Source§impl Default for PypiParser
impl Default for PypiParser
Source§fn default() -> PypiParser
fn default() -> PypiParser
Auto Trait Implementations§
impl Freeze for PypiParser
impl RefUnwindSafe for PypiParser
impl Send for PypiParser
impl Sync for PypiParser
impl Unpin for PypiParser
impl UnsafeUnpin for PypiParser
impl UnwindSafe for PypiParser
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more