From 3ee34341872122086db8e958d960cb7539ac4d79 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 8 Jul 2025 10:48:31 -0400 Subject: [PATCH] Auto-generate environment variable references for ty (#19205) ## Summary This PR mirrors the environment variable implementation we have in uv: https://github.com/astral-sh/uv/blob/efc361223c134840b7650fed1ef9ebd9577b5454/crates/uv-static/src/env_vars.rs#L6-L7. See: https://github.com/astral-sh/ty/issues/773. --- .pre-commit-config.yaml | 2 +- Cargo.lock | 21 ++++ Cargo.toml | 2 + crates/ruff_db/Cargo.toml | 1 + crates/ruff_db/src/lib.rs | 5 +- crates/ruff_dev/Cargo.toml | 1 + crates/ruff_dev/src/generate_all.rs | 5 +- .../src/generate_ty_env_vars_reference.rs | 119 ++++++++++++++++++ crates/ruff_dev/src/main.rs | 4 + crates/ty/Cargo.toml | 1 + crates/ty/docs/environment.md | 55 ++++++++ crates/ty/src/lib.rs | 3 +- crates/ty/src/logging.rs | 5 +- crates/ty_macros/Cargo.toml | 21 ++++ crates/ty_macros/src/env_vars.rs | 95 ++++++++++++++ crates/ty_macros/src/lib.rs | 18 +++ crates/ty_python_semantic/Cargo.toml | 2 + .../ty_python_semantic/src/site_packages.rs | 5 +- crates/ty_python_semantic/tests/mdtest.rs | 3 +- crates/ty_static/Cargo.toml | 19 +++ crates/ty_static/src/env_vars.rs | 71 +++++++++++ crates/ty_static/src/lib.rs | 3 + crates/ty_test/Cargo.toml | 1 + crates/ty_test/src/lib.rs | 10 +- 24 files changed, 458 insertions(+), 14 deletions(-) create mode 100644 crates/ruff_dev/src/generate_ty_env_vars_reference.rs create mode 100644 crates/ty/docs/environment.md create mode 100644 crates/ty_macros/Cargo.toml create mode 100644 crates/ty_macros/src/env_vars.rs create mode 100644 crates/ty_macros/src/lib.rs create mode 100644 crates/ty_static/Cargo.toml create mode 100644 crates/ty_static/src/env_vars.rs create mode 100644 crates/ty_static/src/lib.rs diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fdf8d34468..1abbe47ce3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ exclude: | crates/ty_vendored/vendor/.*| crates/ty_project/resources/.*| crates/ty_python_semantic/resources/corpus/.*| - crates/ty/docs/(configuration|rules|cli).md| + crates/ty/docs/(configuration|rules|cli|environment).md| crates/ruff_benchmark/resources/.*| crates/ruff_linter/resources/.*| crates/ruff_linter/src/rules/.*/snapshots/.*| diff --git a/Cargo.lock b/Cargo.lock index 35707662d9..0748e715b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2874,6 +2874,7 @@ dependencies = [ "thiserror 2.0.12", "tracing", "tracing-subscriber", + "ty_static", "web-time", "zip", ] @@ -2917,6 +2918,7 @@ dependencies = [ "tracing-subscriber", "ty", "ty_project", + "ty_static", "url", ] @@ -4165,6 +4167,7 @@ dependencies = [ "ty_project", "ty_python_semantic", "ty_server", + "ty_static", "wild", ] @@ -4186,6 +4189,15 @@ dependencies = [ "ty_vendored", ] +[[package]] +name = "ty_macros" +version = "0.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ty_project" version = "0.0.0" @@ -4268,6 +4280,7 @@ dependencies = [ "thiserror 2.0.12", "tracing", "ty_python_semantic", + "ty_static", "ty_test", "ty_vendored", ] @@ -4299,6 +4312,13 @@ dependencies = [ "ty_vendored", ] +[[package]] +name = "ty_static" +version = "0.0.1" +dependencies = [ + "ty_macros", +] + [[package]] name = "ty_test" version = "0.0.0" @@ -4327,6 +4347,7 @@ dependencies = [ "toml", "tracing", "ty_python_semantic", + "ty_static", "ty_vendored", ] diff --git a/Cargo.toml b/Cargo.toml index 39de81bf4c..6f66d2b334 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,9 +41,11 @@ ruff_workspace = { path = "crates/ruff_workspace" } ty = { path = "crates/ty" } ty_ide = { path = "crates/ty_ide" } +ty_macros = { path = "crates/ty_macros" } ty_project = { path = "crates/ty_project", default-features = false } ty_python_semantic = { path = "crates/ty_python_semantic" } ty_server = { path = "crates/ty_server" } +ty_static = { path = "crates/ty_static" } ty_test = { path = "crates/ty_test" } ty_vendored = { path = "crates/ty_vendored" } diff --git a/crates/ruff_db/Cargo.toml b/crates/ruff_db/Cargo.toml index c8588b4368..ac90e96214 100644 --- a/crates/ruff_db/Cargo.toml +++ b/crates/ruff_db/Cargo.toml @@ -20,6 +20,7 @@ ruff_python_parser = { workspace = true } ruff_python_trivia = { workspace = true } ruff_source_file = { workspace = true, features = ["get-size"] } ruff_text_size = { workspace = true } +ty_static = { workspace = true } anstyle = { workspace = true } arc-swap = { workspace = true } diff --git a/crates/ruff_db/src/lib.rs b/crates/ruff_db/src/lib.rs index 48d9bd173f..b501956807 100644 --- a/crates/ruff_db/src/lib.rs +++ b/crates/ruff_db/src/lib.rs @@ -5,6 +5,7 @@ use ruff_python_ast::PythonVersion; use rustc_hash::FxHasher; use std::hash::BuildHasherDefault; use std::num::NonZeroUsize; +use ty_static::EnvVars; pub mod diagnostic; pub mod display; @@ -50,8 +51,8 @@ pub trait Db: salsa::Database { /// ty can still spawn more threads for other tasks, e.g. to wait for a Ctrl+C signal or /// watching the files for changes. pub fn max_parallelism() -> NonZeroUsize { - std::env::var("TY_MAX_PARALLELISM") - .or_else(|_| std::env::var("RAYON_NUM_THREADS")) + std::env::var(EnvVars::TY_MAX_PARALLELISM) + .or_else(|_| std::env::var(EnvVars::RAYON_NUM_THREADS)) .ok() .and_then(|s| s.parse().ok()) .unwrap_or_else(|| { diff --git a/crates/ruff_dev/Cargo.toml b/crates/ruff_dev/Cargo.toml index 99adef596b..b7068640f5 100644 --- a/crates/ruff_dev/Cargo.toml +++ b/crates/ruff_dev/Cargo.toml @@ -13,6 +13,7 @@ license = { workspace = true } [dependencies] ty = { workspace = true } ty_project = { workspace = true, features = ["schemars"] } +ty_static = { workspace = true } ruff = { workspace = true } ruff_formatter = { workspace = true } ruff_linter = { workspace = true, features = ["schemars"] } diff --git a/crates/ruff_dev/src/generate_all.rs b/crates/ruff_dev/src/generate_all.rs index 4d3fb9577e..f5cefa70ef 100644 --- a/crates/ruff_dev/src/generate_all.rs +++ b/crates/ruff_dev/src/generate_all.rs @@ -4,7 +4,7 @@ use anyhow::Result; use crate::{ generate_cli_help, generate_docs, generate_json_schema, generate_ty_cli_reference, - generate_ty_options, generate_ty_rules, generate_ty_schema, + generate_ty_env_vars_reference, generate_ty_options, generate_ty_rules, generate_ty_schema, }; pub(crate) const REGENERATE_ALL_COMMAND: &str = "cargo dev generate-all"; @@ -44,5 +44,8 @@ pub(crate) fn main(args: &Args) -> Result<()> { generate_ty_options::main(&generate_ty_options::Args { mode: args.mode })?; generate_ty_rules::main(&generate_ty_rules::Args { mode: args.mode })?; generate_ty_cli_reference::main(&generate_ty_cli_reference::Args { mode: args.mode })?; + generate_ty_env_vars_reference::main(&generate_ty_env_vars_reference::Args { + mode: args.mode, + })?; Ok(()) } diff --git a/crates/ruff_dev/src/generate_ty_env_vars_reference.rs b/crates/ruff_dev/src/generate_ty_env_vars_reference.rs new file mode 100644 index 0000000000..8b2127df66 --- /dev/null +++ b/crates/ruff_dev/src/generate_ty_env_vars_reference.rs @@ -0,0 +1,119 @@ +//! Generate the environment variables reference from `ty_static::EnvVars`. + +use std::collections::BTreeSet; +use std::fs; +use std::path::PathBuf; + +use anyhow::bail; +use pretty_assertions::StrComparison; + +use ty_static::EnvVars; + +use crate::generate_all::Mode; + +#[derive(clap::Args)] +pub(crate) struct Args { + #[arg(long, default_value_t, value_enum)] + pub(crate) mode: Mode, +} + +pub(crate) fn main(args: &Args) -> anyhow::Result<()> { + let reference_string = generate(); + let filename = "environment.md"; + let reference_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .parent() + .unwrap() + .join("crates") + .join("ty") + .join("docs") + .join(filename); + + match args.mode { + Mode::DryRun => { + println!("{reference_string}"); + } + Mode::Check => match fs::read_to_string(&reference_path) { + Ok(current) => { + if current == reference_string { + println!("Up-to-date: {filename}"); + } else { + let comparison = StrComparison::new(¤t, &reference_string); + bail!( + "{filename} changed, please run `cargo dev generate-ty-env-vars-reference`:\n{comparison}" + ); + } + } + Err(err) if err.kind() == std::io::ErrorKind::NotFound => { + bail!( + "{filename} not found, please run `cargo dev generate-ty-env-vars-reference`" + ); + } + Err(err) => { + bail!( + "{filename} changed, please run `cargo dev generate-ty-env-vars-reference`:\n{err}" + ); + } + }, + Mode::Write => { + // Ensure the docs directory exists + if let Some(parent) = reference_path.parent() { + fs::create_dir_all(parent)?; + } + + match fs::read_to_string(&reference_path) { + Ok(current) => { + if current == reference_string { + println!("Up-to-date: {filename}"); + } else { + println!("Updating: {filename}"); + fs::write(&reference_path, reference_string.as_bytes())?; + } + } + Err(err) if err.kind() == std::io::ErrorKind::NotFound => { + println!("Updating: {filename}"); + fs::write(&reference_path, reference_string.as_bytes())?; + } + Err(err) => { + bail!( + "{filename} changed, please run `cargo dev generate-ty-env-vars-reference`:\n{err}" + ); + } + } + } + } + + Ok(()) +} + +fn generate() -> String { + let mut output = String::new(); + + output.push_str("# Environment variables\n\n"); + + // Partition and sort environment variables into TY_ and external variables. + let (ty_vars, external_vars): (BTreeSet<_>, BTreeSet<_>) = EnvVars::metadata() + .iter() + .partition(|(var, _)| var.starts_with("TY_")); + + output.push_str("ty defines and respects the following environment variables:\n\n"); + + for (var, doc) in ty_vars { + output.push_str(&render(var, doc)); + } + + output.push_str("## Externally-defined variables\n\n"); + output.push_str("ty also reads the following externally defined environment variables:\n\n"); + + for (var, doc) in external_vars { + output.push_str(&render(var, doc)); + } + + output +} + +/// Render an environment variable and its documentation. +fn render(var: &str, doc: &str) -> String { + format!("### `{var}`\n\n{doc}\n\n") +} diff --git a/crates/ruff_dev/src/main.rs b/crates/ruff_dev/src/main.rs index e598bbc8fe..93aac0ec93 100644 --- a/crates/ruff_dev/src/main.rs +++ b/crates/ruff_dev/src/main.rs @@ -18,6 +18,7 @@ mod generate_json_schema; mod generate_options; mod generate_rules_table; mod generate_ty_cli_reference; +mod generate_ty_env_vars_reference; mod generate_ty_options; mod generate_ty_rules; mod generate_ty_schema; @@ -53,6 +54,8 @@ enum Command { /// Generate a Markdown-compatible listing of configuration options. GenerateOptions, GenerateTyOptions(generate_ty_options::Args), + /// Generate environment variables reference for ty. + GenerateTyEnvVarsReference(generate_ty_env_vars_reference::Args), /// Generate CLI help. GenerateCliHelp(generate_cli_help::Args), /// Generate Markdown docs. @@ -98,6 +101,7 @@ fn main() -> Result { Command::GenerateTyRules(args) => generate_ty_rules::main(&args)?, Command::GenerateOptions => println!("{}", generate_options::generate()), Command::GenerateTyOptions(args) => generate_ty_options::main(&args)?, + Command::GenerateTyEnvVarsReference(args) => generate_ty_env_vars_reference::main(&args)?, Command::GenerateCliHelp(args) => generate_cli_help::main(&args)?, Command::GenerateDocs(args) => generate_docs::main(&args)?, Command::PrintAST(args) => print_ast::main(&args)?, diff --git a/crates/ty/Cargo.toml b/crates/ty/Cargo.toml index 82ff88e515..719559baf0 100644 --- a/crates/ty/Cargo.toml +++ b/crates/ty/Cargo.toml @@ -19,6 +19,7 @@ ruff_python_ast = { workspace = true } ty_python_semantic = { workspace = true } ty_project = { workspace = true, features = ["zstd"] } ty_server = { workspace = true } +ty_static = { workspace = true } anyhow = { workspace = true } argfile = { workspace = true } diff --git a/crates/ty/docs/environment.md b/crates/ty/docs/environment.md new file mode 100644 index 0000000000..30935a8a09 --- /dev/null +++ b/crates/ty/docs/environment.md @@ -0,0 +1,55 @@ +# Environment variables + +ty defines and respects the following environment variables: + +### `TY_LOG` + +If set, ty will use this value as the log level for its `--verbose` output. +Accepts any filter compatible with the `tracing_subscriber` crate. + +For example: + +- `TY_LOG=uv=debug` is the equivalent of `-vv` to the command line +- `TY_LOG=trace` will enable all trace-level logging. + +See the [tracing documentation](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax) +for more. + +### `TY_LOG_PROFILE` + +If set to `"1"` or `"true"`, ty will enable flamegraph profiling. +This creates a `tracing.folded` file that can be used to generate flame graphs +for performance analysis. + +### `TY_MAX_PARALLELISM` + +Specifies an upper limit for the number of tasks ty is allowed to run in parallel. + +For example, how many files should be checked in parallel. +This isn't the same as a thread limit. ty may spawn additional threads +when necessary, e.g. to watch for file system changes or a dedicated UI thread. + +## Externally-defined variables + +ty also reads the following externally defined environment variables: + +### `CONDA_PREFIX` + +Used to detect an activated Conda environment location. +If both `VIRTUAL_ENV` and `CONDA_PREFIX` are present, `VIRTUAL_ENV` will be preferred. + +### `RAYON_NUM_THREADS` + +Specifies an upper limit for the number of threads ty uses when performing work in parallel. +Equivalent to `TY_MAX_PARALLELISM`. + +This is a standard Rayon environment variable. + +### `VIRTUAL_ENV` + +Used to detect an activated virtual environment. + +### `XDG_CONFIG_HOME` + +Path to user-level configuration directory on Unix systems. + diff --git a/crates/ty/src/lib.rs b/crates/ty/src/lib.rs index 81305c45cb..a4670eee5c 100644 --- a/crates/ty/src/lib.rs +++ b/crates/ty/src/lib.rs @@ -4,6 +4,7 @@ mod python_version; mod version; pub use args::Cli; +use ty_static::EnvVars; use std::io::{self, BufWriter, Write, stdout}; use std::process::{ExitCode, Termination}; @@ -144,7 +145,7 @@ fn run_check(args: CheckCommand) -> anyhow::Result { }; let mut stdout = stdout().lock(); - match std::env::var("TY_MEMORY_REPORT").as_deref() { + match std::env::var(EnvVars::TY_MEMORY_REPORT).as_deref() { Ok("short") => write!(stdout, "{}", db.salsa_memory_dump().display_short())?, Ok("mypy_primer") => write!(stdout, "{}", db.salsa_memory_dump().display_mypy_primer())?, Ok("full") => write!(stdout, "{}", db.salsa_memory_dump().display_full())?, diff --git a/crates/ty/src/logging.rs b/crates/ty/src/logging.rs index ce10e183e8..4ba5da3f74 100644 --- a/crates/ty/src/logging.rs +++ b/crates/ty/src/logging.rs @@ -12,6 +12,7 @@ use tracing_subscriber::filter::LevelFilter; use tracing_subscriber::fmt::format::Writer; use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields}; use tracing_subscriber::registry::LookupSpan; +use ty_static::EnvVars; /// Logging flags to `#[command(flatten)]` into your CLI #[derive(clap::Args, Debug, Clone, Default)] @@ -84,7 +85,7 @@ pub(crate) fn setup_tracing( use tracing_subscriber::prelude::*; // The `TY_LOG` environment variable overrides the default log level. - let filter = if let Ok(log_env_variable) = std::env::var("TY_LOG") { + let filter = if let Ok(log_env_variable) = std::env::var(EnvVars::TY_LOG) { EnvFilter::builder() .parse(log_env_variable) .context("Failed to parse directives specified in TY_LOG environment variable.")? @@ -165,7 +166,7 @@ fn setup_profile() -> ( where S: Subscriber + for<'span> LookupSpan<'span>, { - if let Ok("1" | "true") = std::env::var("TY_LOG_PROFILE").as_deref() { + if let Ok("1" | "true") = std::env::var(EnvVars::TY_LOG_PROFILE).as_deref() { let (layer, guard) = tracing_flame::FlameLayer::with_file("tracing.folded") .expect("Flame layer to be created"); (Some(layer), Some(guard)) diff --git a/crates/ty_macros/Cargo.toml b/crates/ty_macros/Cargo.toml new file mode 100644 index 0000000000..036262285d --- /dev/null +++ b/crates/ty_macros/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "ty_macros" +version = "0.0.0" +edition = { workspace = true } +rust-version = { workspace = true } +homepage = { workspace = true } +documentation = { workspace = true } +repository = { workspace = true } +authors = { workspace = true } +license = { workspace = true } + +[lib] +proc-macro = true + +[lints] +workspace = true + +[dependencies] +proc-macro2 = { workspace = true } +quote = { workspace = true } +syn = { workspace = true } diff --git a/crates/ty_macros/src/env_vars.rs b/crates/ty_macros/src/env_vars.rs new file mode 100644 index 0000000000..da59c67996 --- /dev/null +++ b/crates/ty_macros/src/env_vars.rs @@ -0,0 +1,95 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ImplItem, ItemImpl}; + +pub(crate) fn attribute_env_vars_metadata(mut input: ItemImpl) -> TokenStream { + // Verify that this is an impl for EnvVars + let impl_type = &input.self_ty; + + let mut env_var_entries = Vec::new(); + let mut hidden_vars = Vec::new(); + + // Process each item in the impl block + for item in &mut input.items { + if let ImplItem::Const(const_item) = item { + // Extract the const name and value + let const_name = &const_item.ident; + let const_expr = &const_item.expr; + + // Check if the const has the #[attr_hidden] attribute + let is_hidden = const_item + .attrs + .iter() + .any(|attr| attr.path().is_ident("attr_hidden")); + + // Remove our custom attributes + const_item.attrs.retain(|attr| { + !attr.path().is_ident("attr_hidden") + && !attr.path().is_ident("attr_env_var_pattern") + }); + + if is_hidden { + hidden_vars.push(const_name.clone()); + } else { + // Extract documentation from doc comments + let doc_attrs: Vec<_> = const_item + .attrs + .iter() + .filter(|attr| attr.path().is_ident("doc")) + .collect(); + + if !doc_attrs.is_empty() { + // Convert doc attributes to a single string + let doc_string = extract_doc_string(&doc_attrs); + env_var_entries.push((const_name.clone(), const_expr.clone(), doc_string)); + } + } + } + } + + // Generate the metadata method. + let metadata_entries: Vec<_> = env_var_entries + .iter() + .map(|(_name, expr, doc)| { + quote! { + (#expr, #doc) + } + }) + .collect(); + + let metadata_impl = quote! { + impl #impl_type { + /// Returns metadata for all non-hidden environment variables. + pub fn metadata() -> Vec<(&'static str, &'static str)> { + vec![ + #(#metadata_entries),* + ] + } + } + }; + + quote! { + #input + #metadata_impl + } +} + +/// Extract documentation from doc attributes into a single string +fn extract_doc_string(attrs: &[&syn::Attribute]) -> String { + attrs + .iter() + .filter_map(|attr| { + if let syn::Meta::NameValue(meta) = &attr.meta { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit_str), + .. + }) = &meta.value + { + return Some(lit_str.value().trim().to_string()); + } + } + None + }) + .collect::>() + .join("\n") +} diff --git a/crates/ty_macros/src/lib.rs b/crates/ty_macros/src/lib.rs new file mode 100644 index 0000000000..219228a2db --- /dev/null +++ b/crates/ty_macros/src/lib.rs @@ -0,0 +1,18 @@ +//! This crate implements internal macros for the `ty` library. + +use proc_macro::TokenStream; +use syn::parse_macro_input; + +mod env_vars; + +/// Generates metadata for environment variables declared in the impl block. +/// +/// This attribute macro should be applied to an `impl EnvVars` block. +/// It will generate a `metadata()` method that returns all non-hidden +/// environment variables with their documentation. +#[proc_macro_attribute] +pub fn attribute_env_vars_metadata(_attr: TokenStream, item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as syn::ItemImpl); + + env_vars::attribute_env_vars_metadata(input).into() +} diff --git a/crates/ty_python_semantic/Cargo.toml b/crates/ty_python_semantic/Cargo.toml index a4d5c6a6eb..7c914c29c4 100644 --- a/crates/ty_python_semantic/Cargo.toml +++ b/crates/ty_python_semantic/Cargo.toml @@ -22,6 +22,7 @@ ruff_source_file = { workspace = true } ruff_text_size = { workspace = true } ruff_python_literal = { workspace = true } ruff_python_trivia = { workspace = true } +ty_static = { workspace = true } anyhow = { workspace = true } bitflags = { workspace = true } @@ -52,6 +53,7 @@ strum_macros = { workspace = true } ruff_db = { workspace = true, features = ["testing", "os"] } ruff_python_parser = { workspace = true } ty_python_semantic = { workspace = true, features = ["testing"] } +ty_static = { workspace = true } ty_test = { workspace = true } ty_vendored = { workspace = true } diff --git a/crates/ty_python_semantic/src/site_packages.rs b/crates/ty_python_semantic/src/site_packages.rs index d83ba72059..a6b2b8214c 100644 --- a/crates/ty_python_semantic/src/site_packages.rs +++ b/crates/ty_python_semantic/src/site_packages.rs @@ -23,6 +23,7 @@ use ruff_python_ast::PythonVersion; use ruff_python_trivia::Cursor; use ruff_source_file::{LineIndex, OneIndexed, SourceCode}; use ruff_text_size::{TextLen, TextRange}; +use ty_static::EnvVars; type SitePackagesDiscoveryResult = Result; @@ -149,7 +150,7 @@ impl PythonEnvironment { PythonEnvironment::new(path, origin, system) } - if let Ok(virtual_env) = system.env_var("VIRTUAL_ENV") { + if let Ok(virtual_env) = system.env_var(EnvVars::VIRTUAL_ENV) { return resolve_environment( system, SystemPath::new(&virtual_env), @@ -158,7 +159,7 @@ impl PythonEnvironment { .map(Some); } - if let Ok(conda_env) = system.env_var("CONDA_PREFIX") { + if let Ok(conda_env) = system.env_var(EnvVars::CONDA_PREFIX) { return resolve_environment( system, SystemPath::new(&conda_env), diff --git a/crates/ty_python_semantic/tests/mdtest.rs b/crates/ty_python_semantic/tests/mdtest.rs index 12ce72ca1f..343ded06c7 100644 --- a/crates/ty_python_semantic/tests/mdtest.rs +++ b/crates/ty_python_semantic/tests/mdtest.rs @@ -1,5 +1,6 @@ use camino::Utf8Path; use dir_test::{Fixture, dir_test}; +use ty_static::EnvVars; use ty_test::OutputFormat; /// See `crates/ty_test/README.md` for documentation on these tests. @@ -19,7 +20,7 @@ fn mdtest(fixture: Fixture<&str>) { let test_name = test_name("mdtest", absolute_fixture_path); - let output_format = if std::env::var("MDTEST_GITHUB_ANNOTATIONS_FORMAT").is_ok() { + let output_format = if std::env::var(EnvVars::MDTEST_GITHUB_ANNOTATIONS_FORMAT).is_ok() { OutputFormat::GitHub } else { OutputFormat::Cli diff --git a/crates/ty_static/Cargo.toml b/crates/ty_static/Cargo.toml new file mode 100644 index 0000000000..0fc33e8360 --- /dev/null +++ b/crates/ty_static/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "ty_static" +version = "0.0.1" +edition = { workspace = true } +rust-version = { workspace = true } +homepage = { workspace = true } +documentation = { workspace = true } +repository = { workspace = true } +authors = { workspace = true } +license = { workspace = true } + +[lib] +doctest = false + +[lints] +workspace = true + +[dependencies] +ty_macros = { workspace = true } diff --git a/crates/ty_static/src/env_vars.rs b/crates/ty_static/src/env_vars.rs new file mode 100644 index 0000000000..399f3fd78a --- /dev/null +++ b/crates/ty_static/src/env_vars.rs @@ -0,0 +1,71 @@ +use ty_macros::attribute_env_vars_metadata; + +/// Declares all environment variable used throughout `ty` and its crates. +pub struct EnvVars; + +#[attribute_env_vars_metadata] +impl EnvVars { + /// If set, ty will use this value as the log level for its `--verbose` output. + /// Accepts any filter compatible with the `tracing_subscriber` crate. + /// + /// For example: + /// + /// - `TY_LOG=uv=debug` is the equivalent of `-vv` to the command line + /// - `TY_LOG=trace` will enable all trace-level logging. + /// + /// See the [tracing documentation](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax) + /// for more. + pub const TY_LOG: &'static str = "TY_LOG"; + + /// If set to `"1"` or `"true"`, ty will enable flamegraph profiling. + /// This creates a `tracing.folded` file that can be used to generate flame graphs + /// for performance analysis. + pub const TY_LOG_PROFILE: &'static str = "TY_LOG_PROFILE"; + + /// Control memory usage reporting format after ty execution. + /// + /// Accepted values: + /// + /// * `short` - Display short memory report + /// * `mypy_primer` - Display mypy_primer format and suppress workspace diagnostics + /// * `full` - Display full memory report + #[attr_hidden] + pub const TY_MEMORY_REPORT: &'static str = "TY_MEMORY_REPORT"; + + /// Specifies an upper limit for the number of tasks ty is allowed to run in parallel. + /// + /// For example, how many files should be checked in parallel. + /// This isn't the same as a thread limit. ty may spawn additional threads + /// when necessary, e.g. to watch for file system changes or a dedicated UI thread. + pub const TY_MAX_PARALLELISM: &'static str = "TY_MAX_PARALLELISM"; + + /// Used to detect an activated virtual environment. + pub const VIRTUAL_ENV: &'static str = "VIRTUAL_ENV"; + + /// Used to detect an activated Conda environment location. + /// If both `VIRTUAL_ENV` and `CONDA_PREFIX` are present, `VIRTUAL_ENV` will be preferred. + pub const CONDA_PREFIX: &'static str = "CONDA_PREFIX"; + + /// Filter which tests to run in mdtest. + /// + /// Only tests whose names contain this filter string will be executed. + #[attr_hidden] + pub const MDTEST_TEST_FILTER: &'static str = "MDTEST_TEST_FILTER"; + + /// Switch mdtest output format to GitHub Actions annotations. + /// + /// If set (to any value), mdtest will output errors in GitHub Actions format. + #[attr_hidden] + pub const MDTEST_GITHUB_ANNOTATIONS_FORMAT: &'static str = "MDTEST_GITHUB_ANNOTATIONS_FORMAT"; + + // Externally defined environment variables + + /// Specifies an upper limit for the number of threads ty uses when performing work in parallel. + /// Equivalent to `TY_MAX_PARALLELISM`. + /// + /// This is a standard Rayon environment variable. + pub const RAYON_NUM_THREADS: &'static str = "RAYON_NUM_THREADS"; + + /// Path to user-level configuration directory on Unix systems. + pub const XDG_CONFIG_HOME: &'static str = "XDG_CONFIG_HOME"; +} diff --git a/crates/ty_static/src/lib.rs b/crates/ty_static/src/lib.rs new file mode 100644 index 0000000000..153591db70 --- /dev/null +++ b/crates/ty_static/src/lib.rs @@ -0,0 +1,3 @@ +pub use env_vars::*; + +mod env_vars; diff --git a/crates/ty_test/Cargo.toml b/crates/ty_test/Cargo.toml index f3d698f21f..97bd4bea2c 100644 --- a/crates/ty_test/Cargo.toml +++ b/crates/ty_test/Cargo.toml @@ -19,6 +19,7 @@ ruff_source_file = { workspace = true } ruff_text_size = { workspace = true } ruff_python_ast = { workspace = true } ty_python_semantic = { workspace = true, features = ["serde", "testing"] } +ty_static = { workspace = true } ty_vendored = { workspace = true } anyhow = { workspace = true } diff --git a/crates/ty_test/src/lib.rs b/crates/ty_test/src/lib.rs index 50670fe1cb..827d6faaef 100644 --- a/crates/ty_test/src/lib.rs +++ b/crates/ty_test/src/lib.rs @@ -29,7 +29,7 @@ mod diagnostic; mod matcher; mod parser; -const MDTEST_TEST_FILTER: &str = "MDTEST_TEST_FILTER"; +use ty_static::EnvVars; /// Run `path` as a markdown test suite with given `title`. /// @@ -53,7 +53,7 @@ pub fn run( let mut db = db::Db::setup(); - let filter = std::env::var(MDTEST_TEST_FILTER).ok(); + let filter = std::env::var(EnvVars::MDTEST_TEST_FILTER).ok(); let mut any_failures = false; for test in suite.tests() { if filter @@ -105,10 +105,12 @@ pub fn run( if output_format.is_cli() { println!( - "\nTo rerun this specific test, set the environment variable: {MDTEST_TEST_FILTER}='{escaped_test_name}'", + "\nTo rerun this specific test, set the environment variable: {}='{escaped_test_name}'", + EnvVars::MDTEST_TEST_FILTER, ); println!( - "{MDTEST_TEST_FILTER}='{escaped_test_name}' cargo test -p ty_python_semantic --test mdtest -- {test_name}", + "{}='{escaped_test_name}' cargo test -p ty_python_semantic --test mdtest -- {test_name}", + EnvVars::MDTEST_TEST_FILTER, ); } }