mirror of https://github.com/astral-sh/uv
Implement an auto-import proof-of-concept
This commit is contained in:
parent
ee35fe34ab
commit
70a669dc25
|
|
@ -268,6 +268,36 @@ version = "1.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "attribute-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0053e96dd3bec5b4879c23a138d6ef26f2cb936c9cdc96274ac2b9ed44b5bb54"
|
||||
dependencies = [
|
||||
"attribute-derive-macro",
|
||||
"derive-where",
|
||||
"manyhow",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attribute-derive-macro"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "463b53ad0fd5b460af4b1915fe045ff4d946d025fb6c4dc3337752eaa980f71b"
|
||||
dependencies = [
|
||||
"collection_literals",
|
||||
"interpolator",
|
||||
"manyhow",
|
||||
"proc-macro-utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote-use",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
|
|
@ -557,6 +587,15 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.11"
|
||||
|
|
@ -742,6 +781,12 @@ dependencies = [
|
|||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "collection_literals"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b3f65b8fb8e88ba339f7d23a390fe1b0896217da05e2a66c584c9b29a91df8"
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
|
|
@ -764,6 +809,20 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"rustversion",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
|
|
@ -989,6 +1048,17 @@ version = "0.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b"
|
||||
|
||||
[[package]]
|
||||
name = "derive-where"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.4.1"
|
||||
|
|
@ -1415,6 +1485,38 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "get-size-derive2"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "028f3cfad7c3e3b1d8d04ef0a1c03576f2d62800803fe1301a4cd262849f2dea"
|
||||
dependencies = [
|
||||
"attribute-derive",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "get-size2"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a09c2043819a3def7bfbb4927e7df96aab0da4cfd8824484b22d0c94e84458e"
|
||||
dependencies = [
|
||||
"compact_str",
|
||||
"get-size-derive2",
|
||||
"hashbrown 0.15.4",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
|
||||
dependencies = [
|
||||
"unicode-width 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
|
|
@ -1942,6 +2044,12 @@ dependencies = [
|
|||
"similar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "interpolator"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8"
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.11.0"
|
||||
|
|
@ -1958,6 +2066,18 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-macro"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.15"
|
||||
|
|
@ -2222,6 +2342,29 @@ dependencies = [
|
|||
"quoted_printable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "manyhow"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587"
|
||||
dependencies = [
|
||||
"manyhow-macros",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "manyhow-macros"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495"
|
||||
dependencies = [
|
||||
"proc-macro-utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown"
|
||||
version = "1.0.0"
|
||||
|
|
@ -2624,6 +2767,44 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.5.0"
|
||||
|
|
@ -2771,6 +2952,17 @@ dependencies = [
|
|||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-utils"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
|
|
@ -2897,6 +3089,28 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote-use"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9619db1197b497a36178cfc736dc96b271fe918875fbf1344c436a7e93d0321e"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"quote-use-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote-use-macros"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82ebfb7faafadc06a7ab141a6f67bcfb24cb8beb158c6fe933f2f035afa99f35"
|
||||
dependencies = [
|
||||
"proc-macro-utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quoted_printable"
|
||||
version = "0.5.1"
|
||||
|
|
@ -3266,7 +3480,7 @@ dependencies = [
|
|||
"log",
|
||||
"roxmltree 0.18.1",
|
||||
"simplecss",
|
||||
"siphasher",
|
||||
"siphasher 0.3.11",
|
||||
"svgtypes 0.9.0",
|
||||
]
|
||||
|
||||
|
|
@ -3285,6 +3499,73 @@ version = "0.20.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_ast"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff#dca594f89fe0f892f8f939e09d799d2974a811d7"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bitflags 2.9.1",
|
||||
"compact_str",
|
||||
"get-size2",
|
||||
"is-macro",
|
||||
"itertools 0.14.0",
|
||||
"memchr",
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_parser"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff#dca594f89fe0f892f8f939e09d799d2974a811d7"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bstr",
|
||||
"compact_str",
|
||||
"get-size2",
|
||||
"memchr",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_trivia",
|
||||
"ruff_text_size",
|
||||
"rustc-hash",
|
||||
"static_assertions",
|
||||
"unicode-ident",
|
||||
"unicode-normalization",
|
||||
"unicode_names2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_trivia"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff#dca594f89fe0f892f8f939e09d799d2974a811d7"
|
||||
dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_source_file"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff#dca594f89fe0f892f8f939e09d799d2974a811d7"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"ruff_text_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_text_size"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff#dca594f89fe0f892f8f939e09d799d2974a811d7"
|
||||
dependencies = [
|
||||
"get-size2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-netrc"
|
||||
version = "0.1.2"
|
||||
|
|
@ -3696,6 +3977,12 @@ version = "0.3.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
|
|
@ -3742,6 +4029,12 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "statrs"
|
||||
version = "0.18.0"
|
||||
|
|
@ -3817,7 +4110,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c9ee29c1407a5b18ccfe5f6ac82ac11bab3b14407e09c209a6c1a32098b19734"
|
||||
dependencies = [
|
||||
"kurbo 0.8.3",
|
||||
"siphasher",
|
||||
"siphasher 0.3.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3827,7 +4120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "98ffacedcdcf1da6579c907279b4f3c5492fbce99fbbf227f5ed270a589c2765"
|
||||
dependencies = [
|
||||
"kurbo 0.9.5",
|
||||
"siphasher",
|
||||
"siphasher 0.3.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4486,6 +4779,15 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-script"
|
||||
version = "0.5.7"
|
||||
|
|
@ -4510,6 +4812,28 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_names2"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1673eca9782c84de5f81b82e4109dcfb3611c8ba0d52930ec4a9478f547b2dd"
|
||||
dependencies = [
|
||||
"phf",
|
||||
"unicode_names2_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode_names2_generator"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91e5b84611016120197efd7dc93ef76774f4e084cd73c9fb3ea4a86c570c56e"
|
||||
dependencies = [
|
||||
"getopts",
|
||||
"log",
|
||||
"phf_codegen",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.11"
|
||||
|
|
@ -4660,6 +4984,7 @@ dependencies = [
|
|||
"tracing-tree",
|
||||
"unicode-width 0.2.1",
|
||||
"url",
|
||||
"uv-analyze",
|
||||
"uv-auth",
|
||||
"uv-build-backend",
|
||||
"uv-build-frontend",
|
||||
|
|
@ -4713,6 +5038,16 @@ dependencies = [
|
|||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uv-analyze"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"ruff_python_ast",
|
||||
"ruff_python_parser",
|
||||
"rustc-hash",
|
||||
"uv-normalize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uv-auth"
|
||||
version = "0.0.1"
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ authors = ["uv"]
|
|||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
uv-analyze = { path = "crates/uv-analyze" }
|
||||
uv-auth = { path = "crates/uv-auth" }
|
||||
uv-build-backend = { path = "crates/uv-build-backend" }
|
||||
uv-build-frontend = { path = "crates/uv-build-frontend" }
|
||||
|
|
@ -72,6 +73,8 @@ uv-virtualenv = { path = "crates/uv-virtualenv" }
|
|||
uv-warnings = { path = "crates/uv-warnings" }
|
||||
uv-workspace = { path = "crates/uv-workspace" }
|
||||
|
||||
ruff_python_ast = { git = "https://github.com/astral-sh/ruff" }
|
||||
ruff_python_parser = { git = "https://github.com/astral-sh/ruff" }
|
||||
anstream = { version = "0.6.15" }
|
||||
anyhow = { version = "1.0.89" }
|
||||
arcstr = { version = "1.2.0" }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "uv-analyze"
|
||||
version = "0.0.1"
|
||||
edition = { workspace = true }
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
uv-normalize = { workspace = true }
|
||||
|
||||
ruff_python_ast = { workspace = true }
|
||||
ruff_python_parser = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use ruff_python_ast::statement_visitor::{StatementVisitor, walk_stmt};
|
||||
use ruff_python_ast::{Stmt, StmtImport, StmtImportFrom};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
/// Extract the inferred requirements from Python source code.
|
||||
///
|
||||
/// Assumes that all non-relative imports in the source code are either standard library modules or
|
||||
/// PyPI packages.
|
||||
pub fn extract_requirements(
|
||||
source: &str,
|
||||
) -> Result<BTreeSet<PackageName>, ruff_python_parser::ParseError> {
|
||||
// Parse the source code into a Python module.
|
||||
let module = ruff_python_parser::parse_module(source)?;
|
||||
|
||||
// Extract the import names from the module.
|
||||
let imports = {
|
||||
let mut imports_visitor = ImportsVisitor::new();
|
||||
imports_visitor.visit_body(module.suite());
|
||||
|
||||
imports_visitor.into_imports()
|
||||
};
|
||||
|
||||
// Map the imports to requirements.
|
||||
let requirements = imports
|
||||
.into_iter()
|
||||
.filter_map(|module| {
|
||||
// Skip standard library modules.
|
||||
if STDLIB.contains(module.as_ref()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Use static mapping for known modules.
|
||||
if let Some(name) = MODULE_MAPPING.lookup(module.as_ref()).cloned() {
|
||||
return Some(name);
|
||||
}
|
||||
|
||||
// Otherwise, treat it as a package name.
|
||||
PackageName::from_str(module).ok()
|
||||
})
|
||||
.collect::<BTreeSet<_>>();
|
||||
|
||||
Ok(requirements)
|
||||
}
|
||||
|
||||
/// A collection of known Python standard library modules.
|
||||
#[derive(Debug)]
|
||||
struct Stdlib<'a>(FxHashSet<&'a str>);
|
||||
|
||||
impl<'a> Stdlib<'a> {
|
||||
/// Generate a [`Stdlib`] from a string representation, with each line containing a module name.
|
||||
fn from_str(source: &'a str) -> Self {
|
||||
let mut stdlib = FxHashSet::default();
|
||||
for line in source.lines() {
|
||||
let module = line.trim();
|
||||
if !module.is_empty() {
|
||||
stdlib.insert(module);
|
||||
}
|
||||
}
|
||||
Self(stdlib)
|
||||
}
|
||||
|
||||
/// Returns `true` if the standard library contains the given module.
|
||||
fn contains(&self, module: &str) -> bool {
|
||||
self.0.contains(module)
|
||||
}
|
||||
}
|
||||
|
||||
/// A mapping from module name to PyPI package name.
|
||||
struct ModuleMap<'a>(FxHashMap<&'a str, PackageName>);
|
||||
|
||||
impl<'a> ModuleMap<'a> {
|
||||
/// Generate a [`ModuleMap`] from a string representation, encoded in `${module}:{package}` format.
|
||||
fn from_str(source: &'a str) -> Self {
|
||||
let mut mapping = FxHashMap::default();
|
||||
for line in source.lines() {
|
||||
if let Some((module, package)) = line.split_once(':') {
|
||||
let module = module.trim();
|
||||
let package = PackageName::from_str(package.trim()).unwrap();
|
||||
mapping.insert(module, package);
|
||||
}
|
||||
}
|
||||
Self(mapping)
|
||||
}
|
||||
|
||||
/// Look up a PyPI package name for a given module name.
|
||||
fn lookup(&self, module: &str) -> Option<&PackageName> {
|
||||
self.0.get(module)
|
||||
}
|
||||
}
|
||||
|
||||
/// A mapping from module name to PyPI package name.
|
||||
static MODULE_MAPPING: LazyLock<ModuleMap> =
|
||||
LazyLock::new(|| ModuleMap::from_str(include_str!("pipreqs/mapping")));
|
||||
|
||||
/// A mapping of known standard library modules to their names.
|
||||
static STDLIB: LazyLock<Stdlib> =
|
||||
LazyLock::new(|| Stdlib::from_str(include_str!("pipreqs/stdlib")));
|
||||
|
||||
/// Extracts the set of global names from a given scope.
|
||||
#[derive(Debug)]
|
||||
struct ImportsVisitor<'a>(FxHashSet<&'a str>);
|
||||
|
||||
impl<'a> ImportsVisitor<'a> {
|
||||
fn new() -> Self {
|
||||
Self(FxHashSet::default())
|
||||
}
|
||||
|
||||
/// Extracts the set of import names from a given scope.
|
||||
fn into_imports(self) -> FxHashSet<&'a str> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StatementVisitor<'a> for ImportsVisitor<'a> {
|
||||
fn visit_stmt(&mut self, stmt: &'a Stmt) {
|
||||
match stmt {
|
||||
Stmt::Import(StmtImport { names, .. }) => {
|
||||
for name in names {
|
||||
let name = name.name.as_str();
|
||||
let module = name.split('.').next().unwrap_or(name);
|
||||
self.0.insert(module);
|
||||
}
|
||||
}
|
||||
Stmt::ImportFrom(StmtImportFrom {
|
||||
names,
|
||||
module: Some(..),
|
||||
level: 0,
|
||||
..
|
||||
}) => {
|
||||
for name in names {
|
||||
let name = name.name.as_str();
|
||||
let module = name.split('.').next().unwrap_or(name);
|
||||
self.0.insert(module);
|
||||
}
|
||||
}
|
||||
_ => walk_stmt(self, stmt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -3537,6 +3537,15 @@ pub struct AddArgs {
|
|||
#[arg(long, short, alias = "requirement", group = "sources", value_parser = parse_file_path)]
|
||||
pub requirements: Vec<PathBuf>,
|
||||
|
||||
/// Auto-detect the requirements from the given Python script.
|
||||
///
|
||||
/// This option is only available when used with `--script`. When enabled, uv will parse the
|
||||
/// given script and map its import statements to requirements based on heuristics.
|
||||
///
|
||||
/// This option is in preview and may change in any future release.
|
||||
#[arg(long, group = "sources")]
|
||||
pub auto: bool,
|
||||
|
||||
/// Constrain versions using the given requirements files.
|
||||
///
|
||||
/// Constraints files are `requirements.txt`-like files that only control the _version_ of a
|
||||
|
|
|
|||
|
|
@ -192,6 +192,24 @@ impl PartialOrd for Requirement {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<PackageName> for Requirement {
|
||||
/// Convert a [`PackageName`] to a [`Requirement`].
|
||||
fn from(name: PackageName) -> Self {
|
||||
Requirement {
|
||||
name,
|
||||
extras: Box::new([]),
|
||||
groups: Box::new([]),
|
||||
marker: MarkerTree::default(),
|
||||
source: RequirementSource::Registry {
|
||||
specifier: VersionSpecifiers::empty(),
|
||||
index: None,
|
||||
conflict: None,
|
||||
},
|
||||
origin: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Requirement> for uv_pep508::Requirement<VerbatimUrl> {
|
||||
/// Convert a [`Requirement`] to a [`uv_pep508::Requirement`].
|
||||
fn from(requirement: Requirement) -> Self {
|
||||
|
|
|
|||
|
|
@ -302,6 +302,11 @@ impl Pep723Script {
|
|||
.and_then(|uv| uv.sources.as_ref())
|
||||
.unwrap_or(&EMPTY)
|
||||
}
|
||||
|
||||
/// Return the source code of the script, excluding the PEP 723 metadata.
|
||||
pub fn source_code(&self) -> String {
|
||||
format!("{}{}", self.prelude, self.postlude)
|
||||
}
|
||||
}
|
||||
|
||||
/// PEP 723 metadata as parsed from a `script` comment block.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ default-run = "uv"
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
uv-analyze = { workspace = true }
|
||||
uv-auth = { workspace = true }
|
||||
uv-build-backend = { workspace = true }
|
||||
uv-build-frontend = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use uv_dispatch::BuildDispatch;
|
|||
use uv_distribution::DistributionDatabase;
|
||||
use uv_distribution_types::{
|
||||
Index, IndexName, IndexUrl, IndexUrls, NameRequirementSpecification, Requirement,
|
||||
RequirementSource, UnresolvedRequirement, VersionId,
|
||||
RequirementSource, UnresolvedRequirement, UnresolvedRequirementSpecification, VersionId,
|
||||
};
|
||||
use uv_fs::{LockedFile, Simplified};
|
||||
use uv_git::GIT_STORE;
|
||||
|
|
@ -74,6 +74,7 @@ pub(crate) async fn add(
|
|||
marker: Option<MarkerTree>,
|
||||
editable: Option<bool>,
|
||||
dependency_type: DependencyType,
|
||||
auto: bool,
|
||||
raw: bool,
|
||||
bounds: Option<AddBoundsKind>,
|
||||
indexes: Vec<Index>,
|
||||
|
|
@ -98,7 +99,11 @@ pub(crate) async fn add(
|
|||
preview: PreviewMode,
|
||||
) -> Result<ExitStatus> {
|
||||
if bounds.is_some() && preview.is_disabled() {
|
||||
warn_user_once!("The bounds option is in preview and may change in any future release.");
|
||||
warn_user_once!("The bounds option is in preview and may change in any future release");
|
||||
}
|
||||
|
||||
if auto && preview.is_disabled() {
|
||||
warn_user_once!("The `--auto` option is in preview and may change in any future release");
|
||||
}
|
||||
|
||||
for source in &requirements {
|
||||
|
|
@ -338,7 +343,7 @@ pub(crate) async fn add(
|
|||
|
||||
// Read the requirements.
|
||||
let RequirementsSpecification {
|
||||
requirements,
|
||||
mut requirements,
|
||||
constraints,
|
||||
..
|
||||
} = RequirementsSpecification::from_sources(
|
||||
|
|
@ -350,6 +355,40 @@ pub(crate) async fn add(
|
|||
)
|
||||
.await?;
|
||||
|
||||
// If `--auto` was provided, infer the requirements from the script.
|
||||
if auto {
|
||||
match target {
|
||||
AddTarget::Script(ref script, ..) => {
|
||||
// Extract the inferred requirements.
|
||||
let names = uv_analyze::extract_requirements(&script.source_code())?;
|
||||
|
||||
if names.is_empty() {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"No requirements found in script `{}`",
|
||||
script.path.user_display().cyan()
|
||||
)?;
|
||||
} else {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Inferred requirements: {}",
|
||||
names.iter().map(|name| name.cyan()).join(", ")
|
||||
)?;
|
||||
}
|
||||
|
||||
// Add the inferred requirements to the list of requirements.
|
||||
for name in names {
|
||||
requirements.push(UnresolvedRequirementSpecification::from(Requirement::from(
|
||||
name,
|
||||
)));
|
||||
}
|
||||
}
|
||||
AddTarget::Project(..) => {
|
||||
bail!("`--auto` is not supported for projects, only scripts")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize any shared state.
|
||||
let state = PlatformState::default();
|
||||
|
||||
|
|
|
|||
|
|
@ -1958,6 +1958,7 @@ async fn run_project(
|
|||
args.marker,
|
||||
args.editable,
|
||||
args.dependency_type,
|
||||
args.auto,
|
||||
args.raw,
|
||||
args.bounds,
|
||||
args.indexes,
|
||||
|
|
|
|||
|
|
@ -1321,6 +1321,7 @@ pub(crate) struct AddSettings {
|
|||
pub(crate) dependency_type: DependencyType,
|
||||
pub(crate) editable: Option<bool>,
|
||||
pub(crate) extras: Vec<ExtraName>,
|
||||
pub(crate) auto: bool,
|
||||
pub(crate) raw: bool,
|
||||
pub(crate) bounds: Option<AddBoundsKind>,
|
||||
pub(crate) rev: Option<String>,
|
||||
|
|
@ -1343,6 +1344,7 @@ impl AddSettings {
|
|||
let AddArgs {
|
||||
packages,
|
||||
requirements,
|
||||
auto,
|
||||
constraints,
|
||||
marker,
|
||||
dev,
|
||||
|
|
@ -1461,6 +1463,7 @@ impl AddSettings {
|
|||
marker,
|
||||
dependency_type,
|
||||
raw,
|
||||
auto,
|
||||
bounds,
|
||||
rev,
|
||||
tag,
|
||||
|
|
|
|||
Loading…
Reference in New Issue