Auto-generate environment variable references for ty (#19205)

## Summary

This PR mirrors the environment variable implementation we have in uv:
efc361223c/crates/uv-static/src/env_vars.rs (L6-L7).

See: https://github.com/astral-sh/ty/issues/773.
This commit is contained in:
Charlie Marsh 2025-07-08 10:48:31 -04:00 committed by GitHub
parent 149350bf39
commit 3ee3434187
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 458 additions and 14 deletions

View file

@ -6,7 +6,7 @@ exclude: |
crates/ty_vendored/vendor/.*| crates/ty_vendored/vendor/.*|
crates/ty_project/resources/.*| crates/ty_project/resources/.*|
crates/ty_python_semantic/resources/corpus/.*| 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_benchmark/resources/.*|
crates/ruff_linter/resources/.*| crates/ruff_linter/resources/.*|
crates/ruff_linter/src/rules/.*/snapshots/.*| crates/ruff_linter/src/rules/.*/snapshots/.*|

21
Cargo.lock generated
View file

@ -2874,6 +2874,7 @@ dependencies = [
"thiserror 2.0.12", "thiserror 2.0.12",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"ty_static",
"web-time", "web-time",
"zip", "zip",
] ]
@ -2917,6 +2918,7 @@ dependencies = [
"tracing-subscriber", "tracing-subscriber",
"ty", "ty",
"ty_project", "ty_project",
"ty_static",
"url", "url",
] ]
@ -4165,6 +4167,7 @@ dependencies = [
"ty_project", "ty_project",
"ty_python_semantic", "ty_python_semantic",
"ty_server", "ty_server",
"ty_static",
"wild", "wild",
] ]
@ -4186,6 +4189,15 @@ dependencies = [
"ty_vendored", "ty_vendored",
] ]
[[package]]
name = "ty_macros"
version = "0.0.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "ty_project" name = "ty_project"
version = "0.0.0" version = "0.0.0"
@ -4268,6 +4280,7 @@ dependencies = [
"thiserror 2.0.12", "thiserror 2.0.12",
"tracing", "tracing",
"ty_python_semantic", "ty_python_semantic",
"ty_static",
"ty_test", "ty_test",
"ty_vendored", "ty_vendored",
] ]
@ -4299,6 +4312,13 @@ dependencies = [
"ty_vendored", "ty_vendored",
] ]
[[package]]
name = "ty_static"
version = "0.0.1"
dependencies = [
"ty_macros",
]
[[package]] [[package]]
name = "ty_test" name = "ty_test"
version = "0.0.0" version = "0.0.0"
@ -4327,6 +4347,7 @@ dependencies = [
"toml", "toml",
"tracing", "tracing",
"ty_python_semantic", "ty_python_semantic",
"ty_static",
"ty_vendored", "ty_vendored",
] ]

View file

@ -41,9 +41,11 @@ ruff_workspace = { path = "crates/ruff_workspace" }
ty = { path = "crates/ty" } ty = { path = "crates/ty" }
ty_ide = { path = "crates/ty_ide" } ty_ide = { path = "crates/ty_ide" }
ty_macros = { path = "crates/ty_macros" }
ty_project = { path = "crates/ty_project", default-features = false } ty_project = { path = "crates/ty_project", default-features = false }
ty_python_semantic = { path = "crates/ty_python_semantic" } ty_python_semantic = { path = "crates/ty_python_semantic" }
ty_server = { path = "crates/ty_server" } ty_server = { path = "crates/ty_server" }
ty_static = { path = "crates/ty_static" }
ty_test = { path = "crates/ty_test" } ty_test = { path = "crates/ty_test" }
ty_vendored = { path = "crates/ty_vendored" } ty_vendored = { path = "crates/ty_vendored" }

View file

@ -20,6 +20,7 @@ ruff_python_parser = { workspace = true }
ruff_python_trivia = { workspace = true } ruff_python_trivia = { workspace = true }
ruff_source_file = { workspace = true, features = ["get-size"] } ruff_source_file = { workspace = true, features = ["get-size"] }
ruff_text_size = { workspace = true } ruff_text_size = { workspace = true }
ty_static = { workspace = true }
anstyle = { workspace = true } anstyle = { workspace = true }
arc-swap = { workspace = true } arc-swap = { workspace = true }

View file

@ -5,6 +5,7 @@ use ruff_python_ast::PythonVersion;
use rustc_hash::FxHasher; use rustc_hash::FxHasher;
use std::hash::BuildHasherDefault; use std::hash::BuildHasherDefault;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use ty_static::EnvVars;
pub mod diagnostic; pub mod diagnostic;
pub mod display; 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 /// ty can still spawn more threads for other tasks, e.g. to wait for a Ctrl+C signal or
/// watching the files for changes. /// watching the files for changes.
pub fn max_parallelism() -> NonZeroUsize { pub fn max_parallelism() -> NonZeroUsize {
std::env::var("TY_MAX_PARALLELISM") std::env::var(EnvVars::TY_MAX_PARALLELISM)
.or_else(|_| std::env::var("RAYON_NUM_THREADS")) .or_else(|_| std::env::var(EnvVars::RAYON_NUM_THREADS))
.ok() .ok()
.and_then(|s| s.parse().ok()) .and_then(|s| s.parse().ok())
.unwrap_or_else(|| { .unwrap_or_else(|| {

View file

@ -13,6 +13,7 @@ license = { workspace = true }
[dependencies] [dependencies]
ty = { workspace = true } ty = { workspace = true }
ty_project = { workspace = true, features = ["schemars"] } ty_project = { workspace = true, features = ["schemars"] }
ty_static = { workspace = true }
ruff = { workspace = true } ruff = { workspace = true }
ruff_formatter = { workspace = true } ruff_formatter = { workspace = true }
ruff_linter = { workspace = true, features = ["schemars"] } ruff_linter = { workspace = true, features = ["schemars"] }

View file

@ -4,7 +4,7 @@ use anyhow::Result;
use crate::{ use crate::{
generate_cli_help, generate_docs, generate_json_schema, generate_ty_cli_reference, 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"; 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_options::main(&generate_ty_options::Args { mode: args.mode })?;
generate_ty_rules::main(&generate_ty_rules::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_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(()) Ok(())
} }

View file

@ -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(&current, &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")
}

View file

@ -18,6 +18,7 @@ mod generate_json_schema;
mod generate_options; mod generate_options;
mod generate_rules_table; mod generate_rules_table;
mod generate_ty_cli_reference; mod generate_ty_cli_reference;
mod generate_ty_env_vars_reference;
mod generate_ty_options; mod generate_ty_options;
mod generate_ty_rules; mod generate_ty_rules;
mod generate_ty_schema; mod generate_ty_schema;
@ -53,6 +54,8 @@ enum Command {
/// Generate a Markdown-compatible listing of configuration options. /// Generate a Markdown-compatible listing of configuration options.
GenerateOptions, GenerateOptions,
GenerateTyOptions(generate_ty_options::Args), GenerateTyOptions(generate_ty_options::Args),
/// Generate environment variables reference for ty.
GenerateTyEnvVarsReference(generate_ty_env_vars_reference::Args),
/// Generate CLI help. /// Generate CLI help.
GenerateCliHelp(generate_cli_help::Args), GenerateCliHelp(generate_cli_help::Args),
/// Generate Markdown docs. /// Generate Markdown docs.
@ -98,6 +101,7 @@ fn main() -> Result<ExitCode> {
Command::GenerateTyRules(args) => generate_ty_rules::main(&args)?, Command::GenerateTyRules(args) => generate_ty_rules::main(&args)?,
Command::GenerateOptions => println!("{}", generate_options::generate()), Command::GenerateOptions => println!("{}", generate_options::generate()),
Command::GenerateTyOptions(args) => generate_ty_options::main(&args)?, 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::GenerateCliHelp(args) => generate_cli_help::main(&args)?,
Command::GenerateDocs(args) => generate_docs::main(&args)?, Command::GenerateDocs(args) => generate_docs::main(&args)?,
Command::PrintAST(args) => print_ast::main(&args)?, Command::PrintAST(args) => print_ast::main(&args)?,

View file

@ -19,6 +19,7 @@ ruff_python_ast = { workspace = true }
ty_python_semantic = { workspace = true } ty_python_semantic = { workspace = true }
ty_project = { workspace = true, features = ["zstd"] } ty_project = { workspace = true, features = ["zstd"] }
ty_server = { workspace = true } ty_server = { workspace = true }
ty_static = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
argfile = { workspace = true } argfile = { workspace = true }

View file

@ -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.

View file

@ -4,6 +4,7 @@ mod python_version;
mod version; mod version;
pub use args::Cli; pub use args::Cli;
use ty_static::EnvVars;
use std::io::{self, BufWriter, Write, stdout}; use std::io::{self, BufWriter, Write, stdout};
use std::process::{ExitCode, Termination}; use std::process::{ExitCode, Termination};
@ -144,7 +145,7 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
}; };
let mut stdout = stdout().lock(); 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("short") => write!(stdout, "{}", db.salsa_memory_dump().display_short())?,
Ok("mypy_primer") => write!(stdout, "{}", db.salsa_memory_dump().display_mypy_primer())?, Ok("mypy_primer") => write!(stdout, "{}", db.salsa_memory_dump().display_mypy_primer())?,
Ok("full") => write!(stdout, "{}", db.salsa_memory_dump().display_full())?, Ok("full") => write!(stdout, "{}", db.salsa_memory_dump().display_full())?,

View file

@ -12,6 +12,7 @@ use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::fmt::format::Writer; use tracing_subscriber::fmt::format::Writer;
use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields}; use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields};
use tracing_subscriber::registry::LookupSpan; use tracing_subscriber::registry::LookupSpan;
use ty_static::EnvVars;
/// Logging flags to `#[command(flatten)]` into your CLI /// Logging flags to `#[command(flatten)]` into your CLI
#[derive(clap::Args, Debug, Clone, Default)] #[derive(clap::Args, Debug, Clone, Default)]
@ -84,7 +85,7 @@ pub(crate) fn setup_tracing(
use tracing_subscriber::prelude::*; use tracing_subscriber::prelude::*;
// The `TY_LOG` environment variable overrides the default log level. // 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() EnvFilter::builder()
.parse(log_env_variable) .parse(log_env_variable)
.context("Failed to parse directives specified in TY_LOG environment variable.")? .context("Failed to parse directives specified in TY_LOG environment variable.")?
@ -165,7 +166,7 @@ fn setup_profile<S>() -> (
where where
S: Subscriber + for<'span> LookupSpan<'span>, 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") let (layer, guard) = tracing_flame::FlameLayer::with_file("tracing.folded")
.expect("Flame layer to be created"); .expect("Flame layer to be created");
(Some(layer), Some(guard)) (Some(layer), Some(guard))

View file

@ -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 }

View file

@ -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::<Vec<_>>()
.join("\n")
}

View file

@ -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()
}

View file

@ -22,6 +22,7 @@ ruff_source_file = { workspace = true }
ruff_text_size = { workspace = true } ruff_text_size = { workspace = true }
ruff_python_literal = { workspace = true } ruff_python_literal = { workspace = true }
ruff_python_trivia = { workspace = true } ruff_python_trivia = { workspace = true }
ty_static = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
bitflags = { workspace = true } bitflags = { workspace = true }
@ -52,6 +53,7 @@ strum_macros = { workspace = true }
ruff_db = { workspace = true, features = ["testing", "os"] } ruff_db = { workspace = true, features = ["testing", "os"] }
ruff_python_parser = { workspace = true } ruff_python_parser = { workspace = true }
ty_python_semantic = { workspace = true, features = ["testing"] } ty_python_semantic = { workspace = true, features = ["testing"] }
ty_static = { workspace = true }
ty_test = { workspace = true } ty_test = { workspace = true }
ty_vendored = { workspace = true } ty_vendored = { workspace = true }

View file

@ -23,6 +23,7 @@ use ruff_python_ast::PythonVersion;
use ruff_python_trivia::Cursor; use ruff_python_trivia::Cursor;
use ruff_source_file::{LineIndex, OneIndexed, SourceCode}; use ruff_source_file::{LineIndex, OneIndexed, SourceCode};
use ruff_text_size::{TextLen, TextRange}; use ruff_text_size::{TextLen, TextRange};
use ty_static::EnvVars;
type SitePackagesDiscoveryResult<T> = Result<T, SitePackagesDiscoveryError>; type SitePackagesDiscoveryResult<T> = Result<T, SitePackagesDiscoveryError>;
@ -149,7 +150,7 @@ impl PythonEnvironment {
PythonEnvironment::new(path, origin, system) 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( return resolve_environment(
system, system,
SystemPath::new(&virtual_env), SystemPath::new(&virtual_env),
@ -158,7 +159,7 @@ impl PythonEnvironment {
.map(Some); .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( return resolve_environment(
system, system,
SystemPath::new(&conda_env), SystemPath::new(&conda_env),

View file

@ -1,5 +1,6 @@
use camino::Utf8Path; use camino::Utf8Path;
use dir_test::{Fixture, dir_test}; use dir_test::{Fixture, dir_test};
use ty_static::EnvVars;
use ty_test::OutputFormat; use ty_test::OutputFormat;
/// See `crates/ty_test/README.md` for documentation on these tests. /// 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 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 OutputFormat::GitHub
} else { } else {
OutputFormat::Cli OutputFormat::Cli

View file

@ -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 }

View file

@ -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";
}

View file

@ -0,0 +1,3 @@
pub use env_vars::*;
mod env_vars;

View file

@ -19,6 +19,7 @@ ruff_source_file = { workspace = true }
ruff_text_size = { workspace = true } ruff_text_size = { workspace = true }
ruff_python_ast = { workspace = true } ruff_python_ast = { workspace = true }
ty_python_semantic = { workspace = true, features = ["serde", "testing"] } ty_python_semantic = { workspace = true, features = ["serde", "testing"] }
ty_static = { workspace = true }
ty_vendored = { workspace = true } ty_vendored = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }

View file

@ -29,7 +29,7 @@ mod diagnostic;
mod matcher; mod matcher;
mod parser; mod parser;
const MDTEST_TEST_FILTER: &str = "MDTEST_TEST_FILTER"; use ty_static::EnvVars;
/// Run `path` as a markdown test suite with given `title`. /// Run `path` as a markdown test suite with given `title`.
/// ///
@ -53,7 +53,7 @@ pub fn run(
let mut db = db::Db::setup(); 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; let mut any_failures = false;
for test in suite.tests() { for test in suite.tests() {
if filter if filter
@ -105,10 +105,12 @@ pub fn run(
if output_format.is_cli() { if output_format.is_cli() {
println!( 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!( 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,
); );
} }
} }